React中如何实现点击按钮重新触发useEffect执行?
你把removeFromCart作为useEffect依赖导致接口反复调用的核心原因是:组件每次渲染时,removeFromCart都会生成一个新的函数引用,只要依赖项变化useEffect就会执行,就会反复请求后端接口。
下面是3种可行的解决方案:
方案1:新增触发标识依赖(最直接满足「触发useEffect执行」的需求)
新增一个数字类型的状态作为useEffect的额外依赖,需要触发useEffect时就修改这个状态的值即可:
import React, { useContext, useEffect, useState,useCallback } from "react"; import { UserContext } from "./Context"; import { useHistory } from "react-router"; import cartImage from "../assets/man-shopping.png"; import Axios from "axios"; const Cart = () => { const { user, serUser } = useContext(UserContext); const history = useHistory(); const [product, setProduct] = useState([]); // 新增触发标识 const [cartRefresh, setCartRefresh] = useState(0); const removeFromCart = (item) => { Axios.delete(`http://localhost:3002/cart/${item.Id}`).then((res) => { // 删除成功后修改触发标识,让useEffect重新执行 setCartRefresh(prev => prev + 1); }); }; useEffect(() => { // 边界判断,用户未登录时不请求 if(!user?.Id) return; Axios.get(`http://localhost:3002/cart/${user.Id}`).then((res) => { setProduct(res.data); }); // 依赖新增cartRefresh }, [user, cartRefresh]); // 原有return渲染逻辑保持不变,此处省略
这种方案的好处是,不管是添加商品、删除商品还是其他需要刷新购物车的场景,只需要调用setCartRefresh(prev => prev +1)就能触发接口重新拉取,逻辑统一。
方案2:直接封装拉取逻辑手动调用(适配当前场景的最优解)
其实不需要强制触发useEffect,完全可以把拉取购物车的逻辑单独抽出来,删除成功后直接调用即可:
const Cart = () => { const { user, serUser } = useContext(UserContext); const history = useHistory(); const [product, setProduct] = useState([]); // 抽离拉取购物车逻辑,用useCallback保证引用稳定 const fetchCart = useCallback(() => { if(!user?.Id) return; Axios.get(`http://localhost:3002/cart/${user.Id}`).then((res) => { setProduct(res.data); }); }, [user]); const removeFromCart = (item) => { Axios.delete(`http://localhost:3002/cart/${item.Id}`).then((res) => { // 删除成功直接调用拉取接口 fetchCart(); }); }; // 组件挂载和user变化时拉取一次 useEffect(() => { fetchCart(); }, [fetchCart]); // 原有return渲染逻辑保持不变,此处省略
这个方案避免了多余的状态,逻辑更直观,性能也更好。
方案3:修复依赖函数引用问题(解决你之前尝试的方案的bug)
如果你还是想把removeFromCart加入依赖,只需要用useCallback包裹这个函数,保证它的引用稳定即可:
// 用useCallback包裹,保证函数引用稳定 const removeFromCart = useCallback((item) => { Axios.delete(`http://localhost:3002/cart/${item.Id}`).then((res) => { // setProduct用函数式更新,不需要依赖外部product变量 setProduct(prevProduct => prevProduct.filter((item) => { return item.Id !== res.data.Id; })); }); }, []); useEffect(() => { if(!user?.Id) return; Axios.get(`http://localhost:3002/cart/${user.Id}`).then((res) => { setProduct(res.data); }); }, [user, removeFromCart]);
注意这里setProduct用了函数式更新,所以removeFromCart的依赖项为空,引用不会随渲染变化,就不会导致useEffect反复触发。
内容的提问来源于stack exchange,提问作者gulam ahmad siddique
相关产品推荐
相关产品推荐

