You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 13:25:42