React类组件componentDidUpdate中实现网络请求节流的方法
问题描述
假设我有一个Search组件,当用户输入内容时,会触发API请求检索输入的内容。使用React的useEffect Hook时,结合setTimeout与clearTimeout(将clearTimeout逻辑放置在useEffect返回的清理函数中),即可轻松实现API请求效果。注:该写法实际为停止输入后延迟发请求的防抖效果,和固定间隔触发的节流有区别。但在类组件版本的Search中,该如何在componentDidUpdate生命周期方法内实现同等的效果呢?
已有的Hook版本实现代码如下:
function Search() { const [term, setTerm] = React.useState(""); const [results, setResults] = React.useState([]); React.useEffect(() => { if(term) const timeOutId = window.setTimeout(() => { if (term) (async () => { const { data } = await axios.get( "https://en.wikipedia.org/w/api.php", { params: { action: "query", list: "search", origin: "*", format: "json", srsearch: term, }, } ); setResults(data.query.search); })(); }, 500); } return () => window.clearTimeout(timeoutId); }, [term]); return ( /* some JSX */ ); }
待补全的类组件骨架:
class Search extends React.Component { state = { term: '', results: [] } componentDidUpdate(prevProps, prevState) { /* ??? */ } render() { return ( /* some JSX */ ); } }
实现方案
核心逻辑和Hook版本完全对齐:每次输入关键词变化时,先清除之前未执行的请求定时器,等500ms内没有新输入时再发起API请求;组件卸载时也要清除待执行的定时器,避免内存泄漏。
类组件中不需要把定时器ID存在state里(存state会触发无意义的重渲染),直接挂载到组件实例属性上存储即可,完整实现代码如下:
class Search extends React.Component { state = { term: '', results: [] } // 实例属性存储定时器ID,生命周期和组件实例绑定 timeoutId = null; componentDidUpdate(prevProps, prevState) { // 仅当搜索关键词term变化时才执行后续逻辑,避免其他state更新触发多余操作 if (prevState.term !== this.state.term) { // 清除上一个未执行的定时器,等价于useEffect返回的清理函数逻辑 clearTimeout(this.timeoutId); if (this.state.term) { this.timeoutId = setTimeout(() => { // 发起搜索请求 (async () => { const { data } = await axios.get( "https://en.wikipedia.org/w/api.php", { params: { action: "query", list: "search", origin: "*", format: "json", srsearch: this.state.term, }, } ); this.setState({ results: data.query.search }); })(); }, 500); } } } componentWillUnmount() { // 组件卸载时清除定时器,对应Hook卸载时执行清理的逻辑 clearTimeout(this.timeoutId); } render() { return ( /* 业务对应的JSX内容 */ ); } }
关键说明
- 用实例属性存定时器ID不会触发重渲染,性能远优于将ID存在state中,完全满足跨生命周期存值的需求。
- 必须加
prevState.term !== this.state.term的判断,否则组件因其他状态(比如results更新)触发重渲染执行componentDidUpdate时,会重复重置定时器,导致请求迟迟无法发出。 - 不能省略
componentWillUnmount里的清除逻辑:Hook的清理函数会在组件卸载时自动执行,类组件需要手动处理卸载时的定时器清理,否则组件卸载后定时器回调执行时调用setState会触发内存泄漏警告。 - 补充:给出的Hook示例代码存在作用域问题:
timeOutId定义在if(term)块内部,外层清理函数访问该变量会报引用错误,实际使用时需要把变量声明提到if判断外层。
内容的提问来源于stack exchange,提问作者muhsen97
相关产品推荐
相关产品推荐

