React子组件传递函数后API多次调用:如何实现单次调用?
问题描述
我有一个包含多个ChildComponent标签的页面,希望用户点击不同的ChildComponent时,该组件能调用对应的API,点击操作会打开一个模态框。目前发现打开模态框时API会调用两次,关闭模态框时API还会再调用一次。已知React的这种重复调用行为是正常的,想请教有没有其他架构方式能实现axios API仅调用一次?
相关代码
ParentComponent 代码
const [posts, setPosts] = useState([]); export default function ParentComponent() { const fetchPosts = useCallback(async () => { try { const result = await axios(`https://jsonplaceholder.typicode.com/posts`); setPosts(result.data.data); } catch (error) { console.log(error); } }, []); }
父组件中调用 ChildComponent
<ChildComponent fetchPosts={fetchPosts} onClick={handleOnclick} />
ChildComponent 代码
const ChildComponent = ({ fetchPosts }) => { useEffect( () => props.fetchPosts, [props.fetchPosts] ); } export default memo(ChildComponent);
解决方案
核心问题在于你把API调用放在了useEffect里,而useEffect会在组件挂载、依赖项变化时触发,加上模态框的显示/隐藏会导致组件重新渲染,就会重复触发API调用。要实现点击时仅调用一次API,可以按以下方式调整:
直接在点击事件中触发API调用
把API调用逻辑从ChildComponent的useEffect中移除,转而在父组件的点击事件里直接调用API,再打开模态框:export default function ParentComponent() { const [posts, setPosts] = useState([]); const [isModalOpen, setIsModalOpen] = useState(false); const fetchPosts = useCallback(async () => { try { const result = await axios(`https://jsonplaceholder.typicode.com/posts`); setPosts(result.data.data); } catch (error) { console.log(error); } }, []); const handleOnclick = async () => { await fetchPosts(); setIsModalOpen(true); }; return ( <> <ChildComponent onClick={handleOnclick} /> {isModalOpen && <Modal onClose={() => setIsModalOpen(false)} />} </> ); }子组件简化为仅触发点击事件:
const ChildComponent = ({ onClick }) => { return <button onClick={onClick}>打开模态框</button>; }; export default memo(ChildComponent);添加调用状态锁,防止重复触发
如果必须保留useEffect的方式,可以在父组件中加一个状态标记是否正在调用API,避免重复执行:export default function ParentComponent() { const [posts, setPosts] = useState([]); const [isFetching, setIsFetching] = useState(false); const fetchPosts = useCallback(async () => { if (isFetching) return; setIsFetching(true); try { const result = await axios(`https://jsonplaceholder.typicode.com/posts`); setPosts(result.data.data); } catch (error) { console.log(error); } finally { setIsFetching(false); } }, [isFetching]); // 后续逻辑保持不变 }将API逻辑抽离到自定义Hook
把API调用封装成独立的自定义Hook,统一管理调用状态和结果,避免组件重复触发:function usePosts() { const [posts, setPosts] = useState([]); const [isFetching, setIsFetching] = useState(false); const fetchPosts = useCallback(async () => { if (isFetching) return; setIsFetching(true); try { const result = await axios(`https://jsonplaceholder.typicode.com/posts`); setPosts(result.data.data); } catch (error) { console.log(error); } finally { setIsFetching(false); } }, [isFetching]); return { posts, fetchPosts, isFetching }; } // 父组件中使用 export default function ParentComponent() { const { posts, fetchPosts } = usePosts(); const [isModalOpen, setIsModalOpen] = useState(false); const handleOnclick = async () => { await fetchPosts(); setIsModalOpen(true); }; return ( <> <ChildComponent onClick={handleOnclick} /> {isModalOpen && <Modal posts={posts} onClose={() => setIsModalOpen(false)} />} </> ); }
内容的提问来源于stack exchange,提问作者Ilyas Patel
相关产品推荐
相关产品推荐

