如何在不重复函数的情况下访问组件useEffect内的函数?
如何在useEffect外部调用内部的fetchBooks函数实现重试?
完全不用把fetchBooks提升到应用层级,这里有两个更贴合需求的解决办法:
方案一:将函数抽离到组件顶层,用useRef管理请求控制器
把fetchBooks定义在组件内部的顶层(不是应用全局),同时用useRef保存AbortController实例,这样既能在首次渲染时通过useEffect调用,也能在组件其他地方(比如失败提示的重试按钮)触发,还保留了请求取消的能力。
const BooksComponent = () => { const [books, setBooks] = useState([]); const [load, setLoad] = useState({ type: "page", ongoing: false }); // 用ref保存控制器,方便后续取消未完成的请求 const abortControllerRef = useRef(null); // 把fetchBooks定义在组件顶层,脱离useEffect作用域 const fetchBooks = async () => { // 先取消之前未完成的请求,避免重复请求 if (abortControllerRef.current) { abortControllerRef.current.abort(); } abortControllerRef.current = new AbortController(); try { setLoad({ type: "page", ongoing: true }); const response = await axios({ method: "GET", url: "https://myURL/api/v1/products/books", signal: abortControllerRef.current.signal, }); setBooks(response.data.result); } catch (e) { // 排除主动取消请求的情况,只处理真实的请求失败 if (e.name !== 'AbortError') { console.log("书籍加载失败,可点击重试"); } } finally { setLoad({ type: "page", ongoing: false }); } }; // 首次渲染自动调用 useEffect(() => { fetchBooks(); // 组件卸载时取消请求 return () => { abortControllerRef.current?.abort(); }; }, []); return ( <div> {load.ongoing && <div>加载中...</div>} {/* 点击按钮触发重试 */} <button onClick={fetchBooks} disabled={load.ongoing}>重新加载书籍</button> {/* 书籍列表渲染逻辑 */} </div> ); };
方案二:在catch块内实现自动重试逻辑
如果只需要请求失败后自动重试,不需要手动触发,完全可以在useEffect内部的catch块里直接处理,不用修改函数位置。
useEffect(() => { const controller = new AbortController(); const maxRetries = 3; // 限制最大重试次数 let retryCount = 0; const fetchBooks = async () => { try { setLoad({ type: "page", ongoing: true }); const response = await axios({ method: "GET", url: "https://myURL/api/v1/products/books", signal: controller.signal, }); setBooks(response.data.result); } catch (e) { // 排除主动取消的情况,且未达到最大重试次数时触发重试 if (e.name !== 'AbortError' && retryCount < maxRetries) { retryCount++; console.log(`第${retryCount}次重试`); fetchBooks(); } else { console.log("请求最终失败,停止重试"); } } finally { setLoad({ type: "page", ongoing: false }); } }; fetchBooks(); return () => { controller.abort(); console.log("component unmounts"); }; }, []);
总结
不需要把函数提升到应用层级,根据你的需求选择方案:
- 需要手动触发重试(比如用户点击按钮):选方案一,把函数抽离到组件顶层并管理好请求控制器;
- 只需要失败后自动重试:选方案二,直接在catch块内添加重试逻辑。
内容的提问来源于stack exchange,提问作者Jesse Moses
相关产品推荐
相关产品推荐

