React Hook useEffect不必要依赖警告的解决方法咨询
代码实现
const [employeeListUpdated, setEmployeeListUpdated] = useState(false); const useAllEmployees = () => { const [employees, setEmployees] = useState([]); useEffect(() => { const fetchEmployees = async () => { try { const response = await axios.get(API_URL, { headers: { Authorization: `${bearerToken}`, }, }); console.log(response); setEmployees(response.data); } catch (error) { console.error("Failed to make request: ", error.message); } }; fetchEmployees(); }, [employeeListUpdated]); const employeesWithBlood = employees.map((x) => ({ ...x, blood: bloods[x.bloodId - 1]?.title, relation: relations[x.relationxId - 1]?.title, })); return employeesWithBlood; };
问题描述
我希望在employeeListUpdated的值变化时重新获取员工列表,该状态会在员工增删改数据库操作时更新,以此保持员工列表同步。但收到React Hook警告:
React Hook useEffect has an unnecessary dependency: 'employeeListUpdated'. Either exclude it or remove the dependency array. Outer scope values like 'employeeListUpdated' aren't valid dependencies because mutating them doesn't re-render the component react-hooks/exhaustive-deps
请问该如何解决这个问题?
解决方案
方法一:将刷新逻辑封装到自定义Hook内部(推荐)
把触发刷新的状态移到useAllEmployees Hook里,同时暴露一个手动刷新的函数,外部在增删改操作完成后调用这个函数即可触发重新请求,完全规避外部状态依赖的问题:
const useAllEmployees = () => { const [employees, setEmployees] = useState([]); // 用递增数字作为刷新触发标识,比布尔值更可靠 const [refreshCount, setRefreshCount] = useState(0); useEffect(() => { const fetchEmployees = async () => { try { const response = await axios.get(API_URL, { headers: { Authorization: `${bearerToken}`, }, }); setEmployees(response.data); } catch (error) { console.error("Failed to make request: ", error.message); } }; fetchEmployees(); }, [refreshCount, bearerToken]); // 补充bearerToken作为依赖,避免遗漏 // 暴露刷新函数给外部调用 const refreshEmployees = () => { setRefreshCount(prev => prev + 1); }; const employeesWithBlood = employees.map((x) => ({ ...x, blood: bloods[x.bloodId - 1]?.title, relation: relations[x.relationxId - 1]?.title, })); // 返回处理后的列表和刷新函数 return { employeesWithBlood, refreshEmployees }; };
外部组件使用示例:
const { employeesWithBlood, refreshEmployees } = useAllEmployees(); // 新增员工后调用刷新 const handleAddEmployee = async () => { await apiAddEmployee(); refreshEmployees(); };
方法二:将外部状态作为Hook参数传入
如果你想保留外部的employeeListUpdated状态,可以把它作为参数传递给useAllEmployees,这样Hook能正确感知参数变化,lint工具也不会报错:
// 接收外部状态作为参数 const useAllEmployees = (employeeListUpdated) => { const [employees, setEmployees] = useState([]); useEffect(() => { const fetchEmployees = async () => { try { const response = await axios.get(API_URL, { headers: { Authorization: `${bearerToken}`, }, }); setEmployees(response.data); } catch (error) { console.error("Failed to make request: ", error.message); } }; fetchEmployees(); }, [employeeListUpdated, bearerToken]); // 此时参数是合法依赖 const employeesWithBlood = employees.map((x) => ({ ...x, blood: bloods[x.bloodId - 1]?.title, relation: relations[x.relationxId - 1]?.title, })); return employeesWithBlood; };
外部组件使用示例:
const [employeeListUpdated, setEmployeeListUpdated] = useState(false); const employeesWithBlood = useAllEmployees(employeeListUpdated); // 修改员工后切换状态触发刷新 const handleUpdateEmployee = async () => { await apiUpdateEmployee(); setEmployeeListUpdated(prev => !prev); };
警告原因说明
原来的写法报错,是因为employeeListUpdated是定义在自定义Hook外部的组件状态。Hook内部无法直接感知外部状态的变化——只有当组件重渲染时,Hook才会重新执行,但此时Hook里的employeeListUpdated是闭包捕获的旧值,不会触发useEffect重新执行。lint工具识别到这个无效依赖,所以抛出警告。
内容的提问来源于stack exchange,提问作者Siddarth K

