我正在尝试使用反应钩子构建一个 redux 进程,下面的代码是我想模拟一个带有 getUsers(redux action( 调用的ComponentDidMount
函数,这是一个 http 请求来获取数据。
第一个版本是这样的
const { state, actions } = useContext(StoreContext);
const { getUsers } = actions;
useEffect(() => {
getUsers(); // React Hook useEffect has a missing dependency: 'getUsers'.
}, []);
但我收到了一个 linting 警告"React Hook useEffect 缺少依赖项:'getUsers'。要么包含它,要么删除依赖数组"在useEffect中,
然后我将getUser添加到依赖数组中,但在那里得到了无限循环
useEffect(() => {
getUsers();
}, [getUsers])
现在我通过使用useRef找到解决方案
const fetchData = useRef(getUsers);
useEffect(() => {
fetchData.current();
}, []);
不确定这是否是正确的方法,但它确实解决了 linting 和无限循环(暂时?
我的问题是: 在代码的第二个版本中,究竟是什么导致了无限循环?每次渲染后,依赖项数组中的 getUsers 是否会更改?
你的函数有依赖关系,React 认为不列出依赖是不安全的。假设您的函数依赖于一个名为users
的属性。在依赖项数组中显式列出隐式依赖项将不起作用:
useEffect(() => {
getUsers();
}, [users]); // won't work
但是,React 表示,解决此问题的推荐方法是将函数移动到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 中获取该函数的,它没有在您的组件中定义。
那怎么办?解决问题的办法是记住你的函数。
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
返回相同的对象而不创建另一个对象。所以你很可能使用相同的函数。
你应该在 useEffect 中声明 getUsers 它是你唯一调用 getUsers 的地方
useEffect(() => {
const { getUsers } = props;
getUsers();
}, []);
根据我在 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 在首次渲染后运行,组件 DidUpdate 在每次更新后运行,但 useEffect 在每次渲染后运行,因此涵盖了这两种情况。使用效果的第二个可选参数是检查 应该组件更新 在我们的例子中 [todoName]。这基本上检查重新渲染后todoName是否发生了变化,然后只做useEffect函数内的任何操作。 希望这有帮助!