我的目标是当用户在输入中键入内容时发出 API 请求。我成功获取数据了。然而,该组件重新渲染两次并给我这个警告。如果我包含“上下文”,我就会陷入无限循环。这是我的代码:
Component.js:
const SearchBox = () => {
const [searchTerm, setSearchTerm] = useState("");
const { handleSearch, searchResults } = useContext(MovieContext);
console.log(searchResults);
useEffect(() => {
let timer;
timer = setTimeout(() => {
handleSearch(searchTerm);
}, 500);
return () => clearTimeout(timer);
}, [searchTerm]);
const renderResults = () => {
if (searchResults.length > 0) {
searchResults.map(result => {
return (
<div key={result.Title}>
<img src={result.Poster} alt={result.Title} />;
</div>
);
});
}
return;
};
return (
<>
<label>
<b>Search</b>
</label>
<input
className="input"
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
/>
<div className="dropdown is-active">
<div className="dropdown-menu">
<div className="dropdown-content results">{renderResults()}</div>
</div>
</div>
</>
);
};
在此 context.searchResults 之上,未定义,尽管我将初始值设置为空数组。我想知道是什么原因造成的。我究竟做错了什么?下面是我的上下文代码:
Context.js:
const Context = React.createContext("");
export class MovieStore extends Component {
constructor(props) {
super(props);
this.state = {
searchResults: [],
handleSearch: this.handleSearch
};
}
handleSearch = async term => {
try {
if (term !== "") {
const response = await axios.get("http://www.omdbapi.com/", {
params: {
apikey: apikey,
s: term
}
});
this.setState({ searchResults: response.data.Search });
}
} catch (error) {
console.log(error);
}
};
render() {
return (
<Context.Provider value={this.state}>
{this.props.children}
</Context.Provider>
);
}
}
React
文档这里提到了关于无限循环的完全相同的事情。因此,无限循环的原因是,在上下文渲染函数中,您在每次调用渲染时都会创建new值。
render() {
return (
<Context.Provider
// ! value object creates every time render is called - it's bad
value={{ ...this.state, handleSearch: this.handleSearch }}
>
{this.props.children}
</Context.Provider>
);
}
它会导致每个消费者在上下文状态更新时重新渲染。因此,如果将
context
放入
useEffect
的依赖项数组中,最终会导致无限循环,因为 context
的值总是不同的。发生的事情是这样的:
useEffect
看到上下文值已被 更新并调用
setTimeout
,这将要求再次搜索
在 500 毫秒内在上下文提供程序中。
export class MovieStore extends Component {
handleSearch = async term => {
try {
if (term !== "") {
const response = await axios.get("http://www.omdbapi.com/", {
params: {
apikey: "15bfc1e3",
s: term
}
});
this.setState({ searchResults: response.data.Search });
}
} catch (error) {
console.log(error);
}
};
state = {
searchResults: [],
handleSearch: this.handleSearch // <~ put method directly to the state
};
render() {
return (
<Context.Provider value={this.state}> // <~ Just returning state here
{this.props.children}
</Context.Provider>
);
}
}