我正在尝试使用react钩子构建一个redux进程,下面的代码是我想用getUsers(redux action)调用模拟一个ComponentDidMount
函数,这是一个http请求来获取数据。
第一个版本就是这样
const { state, actions } = useContext(StoreContext);
const { getUsers } = actions;
useEffect(() => {
getUsers(); // React Hook useEffect has a missing dependency: 'getUsers'.
}, []);
但是我收到了一条警告:“ React Hook useEffect缺少依赖项:'getUsers'。要么包含它,要么删除依赖项数组”在useEffect中,
然后我将getUsers添加到依赖项数组,但是在那里出现无限循环
useEffect(() => {
getUsers();
}, [getUsers])
现在我可以使用useRef
找到解决方案const fetchData = useRef(getUsers);
useEffect(() => {
fetchData.current();
}, []);
不确定这是否是正确的方法,但确实解决了棉绒和无限循环问题(暂时?)
我的问题是: 在第二版代码中,到底是什么导致了无限循环?每次渲染后,依赖数组中的getUsers是否会发生变化?
答案 0 :(得分:1)
您的函数具有依赖关系,React认为不列出依赖关系是不安全的。假设您的函数取决于名为users
的属性。在Dependencies数组中明确列出隐式依赖项将不起作用:
useEffect(() => {
getUsers();
}, [users]); // won't work
但是,React says that the recommended way to fix this是在useEffect()
函数内部移动该函数。这样,警告不会说它缺少getUsers
依赖项,而是getUsers
所依赖的依赖项。
function Example({ users }) {
useEffect(() => {
// we moved getUsers inside useEffect
function getUsers() {
console.log(users);
}
getUsers();
}, []); // users dependency missing
}
因此您可以指定users
依赖项:
useEffect(() => {
function getUsers() {
console.log(users);
}
getUsers();
}, [users]); // OK
但是,您是从props中获得该功能的,它没有在组件中定义。
那该怎么办?解决您的问题的方法是memoize您的函数。
useCallback将返回回调的记忆版本,仅在其中一个依赖项已更改时才更改。在将回调传递给依赖于引用相等性的优化子组件以防止不必要的渲染(例如,shouldComponentUpdate)时,这很有用。
您无法在组件中记住它,因为会出现相同的警告:
const memoizedGetUsers = useCallback(
() => {
getUsers();
},
[], // same warning, missing the getUsers dependency
);
解决方案是在定义getUsers
的地方记住它,然后您将能够列出依赖项:
// wrap getUsers inside useCallback
const getUsers = useCallback(
() => {
//getUsers' implementation using users
console.log(users);
},
[users], // OK
);
在您的组件中,您将能够执行以下操作:
const { getUsers } = actions; // the memoized version
useEffect(() => {
getUsers();
}, [getUsers]); // it is now safe to set getUsers as a dependency
关于为什么存在无限循环以及useRef起作用的原因。我猜想您的函数会导致重新渲染,并且在每次迭代中都会重新创建getUsers,最终导致无休止的循环。 useRef返回一个对象{ current: ... }
,而使用useRef和自己创建该对象{ current: ... }
之间的区别是useRef
返回相同的对象,并且不会创建另一个对象。因此,您可能使用了相同的功能。
答案 1 :(得分:0)
您应该在useEffect中声明getUsers,这是您唯一调用getUsers的地方
useEffect(() => {
const { getUsers } = props;
getUsers();
}, []);
答案 2 :(得分:0)
根据我在React挂钩上的信息,第二个参数的工作将用作比较变量,因为它应该在shouldComponentUpdate中使用。 据我对问题的理解,getUsers是一个函数,而不是一个可能在某些情况下发生变化并因此发生无限循环的变量。尝试传递一个在调用getUsers之后会改变的道具。
import React, { useState,useEffect } from 'react';
function Input({getInputElement}) {
let [todoName, setTodoName] = useState('');
useEffect (() => {
getInputElement(todoName);
},[todoName]);
return (
<div>
<input id={'itemNameInput'} onChange={(e) => setTodoName(e.target.value)} value={todoName} />
</div>
);
}
export default Input;
useEffect: useEffect是componentDidMount,componentDidUpdate和shouldComponentUpdate的组合。虽然componentDidMount在首次渲染后运行,而componentDidUpdate在每次更新后运行,但useEffect在每次渲染后运行,因此涵盖了两种情况。 useEffect的第二个可选参数是对我们的案例[todoName]中的shouldComponentUpdate的检查。这基本上是检查redoender之后todoName是否已更改,然后仅执行useEffect函数内的任何操作。 希望这会有所帮助!