[英]Handle Click outside Component in React
Currently I have a search bar which toggles "Results" component on formSubmit. 目前我有一个搜索栏,可以在formSubmit上切换“结果”组件。 I am looking for a React approach to handle clicks() outside "results" to hide it.
我正在寻找一种React方法来处理“结果”之外的点击()来隐藏它。 The problem is that when I go to another page, or when I click anywhere my "results" keep showing.
问题是当我去另一个页面,或者当我点击任何地方时,我的“结果”会继续显示。
I've tried using CSS stuff like focusOutside, but that's not my way. 我尝试过使用像focusOutside这样的CSS之类的东西,但这不是我的方式。
Search.js Search.js
import React, {Component} from 'react';
import {Container, Icon} from 'semantic-ui-react';
import {connect} from 'react-redux';
import {searchAll} from './modules/searchAction';
import SearchResult from './SearchResult';
import MyLoader from "../../components/MyLoader";
import "../../styles/layout/_search.scss"
class Search extends Component {
state = {
query: null,
};
handleInputChange = (e) => {
this.setState({query: e.target.value})
};
handleSubmit = (e) => {
e.preventDefault();
this.state.query === null || undefined || '' ?
(alert('wrong input')) :
(this.props.searchAll(this.state.query));
};
render() {
const {error, loading, result} = this.props;
const filteredResult = result.filter(item => item.poster_path && (item.name || item.title));
if (error) {
console.log(error);
}
if (loading) {
return <MyLoader/>;
}
return (
<div className="search_area">
<Container className="primary-container">
<form className='search_form' onSubmit={this.handleSubmit}>
<Icon name='search'
size="large"
className='search_icon'/>
<input type='text'
className='search_input'
placeholder="Search for a Movie, Tv Show or Person"
onChange={this.handleInputChange}/>
</form>
<div className="results_area">
{filteredResult.map(suggestion => {
return (
<SearchResult
key={suggestion.id}
title={suggestion.title}
name={suggestion.name}
release_date={suggestion.release_date}
media_type={suggestion.media_type}
path={suggestion.poster_path}
/>
)
})}
</div>
</Container>
</div>
)
};
}
const mapStateToProps = state => ({
result: state.suggestions.suggestions,
loading: state.suggestions.loading,
error: state.suggestions.error
});
const mapDispatchToProps = {
searchAll,
};
export default connect(mapStateToProps, mapDispatchToProps)(Search);
SearchResult.js SearchResult.js
import React from "react";
import {DEFAULT_IMG_URL} from "../../const";
import {SMALL_IMG} from "../../const";
import {Image} from "semantic-ui-react";
import "../../styles/layout/_search.scss"
const SearchResult = (props) => {
let title = null;
let release = null;
let type = null;
let imageLink = DEFAULT_IMG_URL + SMALL_IMG + props.path;
switch (props.media_type) {
case "movie": {
type = "Movie";
break;
}
case "tv": {
type = "TV";
break;
}
case "person": {
type = "Person";
break;
}
default: {
type = "TBD";
break;
}
}
props.title === undefined ?
(title = "N/A"):
(title = props.title);
props.release_date === undefined ?
(release = "N/A"):
(release = props.release_date);
return (
<div className="suggestion-body">
<Image className="suggestion-image"
src={imageLink}>
</Image>
<div className="suggestion-info">
<div className="suggestion-title">
<h2>{title}</h2>
</div>
<div className="suggestion-year">
<h4>{release}</h4>
</div>
</div>
<div className="suggestion-type">
{type}
</div>
</div>
);
};
export default SearchResult;
searchReducer.js searchReducer.js
import {
SEARCH_ALL_BEGIN,
SEARCH_ALL_SUCCESS,
SEARCH_ALL_FAILURE
} from "./searchAction";
const initialState = {
suggestions: [],
loading: false,
error: null
//suggestions true/false
};
const searchReducer = (state = initialState, action) => {
switch(action.type) {
case SEARCH_ALL_BEGIN:
return {
...state,
loading: true,
error: null
};
case SEARCH_ALL_SUCCESS:
return {
...state,
loading: false,
suggestions: action.suggestions
};
case SEARCH_ALL_FAILURE:
return {
...state,
loading: false,
error: action.error,
};
default:
return state;
}
};
export default searchReducer;
and searchAction.js 和searchAction.js
import axios from 'axios/index';
import {KEY} from "../../../key";
import {DEFAULT_URL} from "../../../const";
export const SEARCH_ALL_BEGIN = 'SEARCH_ALL_BEGIN';
export const SEARCH_ALL_SUCCESS = 'SEARCH_ALL_SUCCESS';
export const SEARCH_ALL_FAILURE = 'SEARCH_ALL_FAILURE';
export const searchAllBegin = () => ({
type: SEARCH_ALL_BEGIN
});
export const searchAllSuccess = suggestions => ({
type: SEARCH_ALL_SUCCESS,
suggestions
});
export const searchAllFailure = error => ({
type: SEARCH_ALL_FAILURE,
error
});
export const searchAll = (query) => {
return dispatch => {
let url = DEFAULT_URL + `search/multi?api_key=` + KEY + `&language=en-US&query=` + query + `&page=1&include_adult=false`;
dispatch(searchAllBegin());
axios.get(url)
.then(result => {
dispatch(searchAllSuccess(result.data.results));
})
.catch(error => dispatch(searchAllFailure()));
};
};
As I see my behavior gives me only two solutions one of is just hiding element and the second is sending a null query, which make no sense to me, there should be a better way? 正如我看到我的行为只给我两个解决方案,其中一个只是隐藏元素,第二个是发送一个空查询,这对我没有意义,应该有更好的方法吗?
You can register EventListener on click to body element at componentDidMount hook. 您可以在componentDidMount挂钩的click to body元素上注册EventListener。 Сheck outside clicks and don't forget remove EventListener at componentWillUnmount hook.
Сheck外部点击,不要忘记在componentWillUnmount钩子上删除EventListener。
You can put an overlay around your search box, something like this: 您可以在搜索框周围放置一个叠加层,如下所示:
// style
.overlay {
background-color: transparent;
width: 100%;
height: 100%;
top: 0;
left: 0;
position: absolute;
z-index: 1200;
}
close(e) {
// this is necesary to no close if the search box is clicked
if (e && e.target !== e.currentTarget) {
return;
}
// my close stuff
}
render() {
return <div className="overlay" style={{height: document.body.scrollHeight}}
onClick={e => this.close(e)}>
<div className="searchbox">
My searchbox stuff...
</div>
</div>
}
I've solved it by two steps, first was adding [ https://github.com/airbnb/react-outside-click-handler ] to my project and adding a isSearchResultsVisible
bool variable. 我已经通过两个步骤解决了这个问题,首先是在我的项目中添加[ https://github.com/airbnb/react-outside-click-handler ]并添加一个
isSearchResultsVisible
bool变量。
Search.js file Search.js文件
import React, {Component} from 'react';
import {Container, Icon} from 'semantic-ui-react';
import {connect} from 'react-redux';
import {searchAll, setSearchResultsVisibility} from './modules/searchAction';
import SearchResult from './SearchResult';
import MyLoader from "../../components/MyLoader";
import "../../styles/layout/_search.scss"
class Search extends Component {
state = {
query: null,
};
handleInputChange = (e) => {
this.setState({query: e.target.value})
};
handleSubmit = (e) => {
e.preventDefault();
if(this.state.query) {
this.props.searchAll(this.state.query);
this.props.setSearchResultsVisibility(true);
}
};
render() {
const {error, loading, result, isSearchResultsVisible} = this.props;
const filteredResult = result.filter(item => item.poster_path && (item.name || item.title));
if (error) {console.log(error)}
if (loading) {return <MyLoader/>}
return (
<div className="search_area">
<Container className="primary-container">
<form className="search_form" onSubmit={this.handleSubmit}>
<Icon name="search"
size="large"
className="search_icon"
/>
<input type="text"
className="search_input"
placeholder="Search for a Movie, Tv Show or Person"
onChange={this.handleInputChange}
/>
</form>
{
isSearchResultsVisible &&
<SearchResult result={filteredResult} />
}
</Container>
</div>
)
};
}
const mapStateToProps = state => ({
result: state.suggestions.suggestions,
loading: state.suggestions.loading,
error: state.suggestions.error,
isSearchResultsVisible: state.suggestions.isSearchResultsVisible,
});
const mapDispatchToProps = {
searchAll,
setSearchResultsVisibility,
};
export default connect(mapStateToProps, mapDispatchToProps)(Search);
add 加
export const SET_SEARCH_RESULTS_VISIBILITY = 'SET_SEARCH_RESULTS_VISIBILITY';
export const setSearchResultsVisibility = isSearchResultsVisible => ({
type: SET_SEARCH_RESULTS_VISIBILITY,
isSearchResultsVisible
});
to action file and 动作文件和
case SET_SEARCH_RESULTS_VISIBILITY:
return {
...state,
isSearchResultsVisible: action.isSearchResultsVisible,
};
to reducer. 减速机。 In case anyone's reading this, don't forget to import
SET_SEARCH_RESULTS_VISIBILITY
to reducer and to add isSearchResultsVisible: false,
to initialState 如果有人在阅读本文,请不要忘记将
SET_SEARCH_RESULTS_VISIBILITY
导入reducer并将isSearchResultsVisible: false,
添加到initialState
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.