![](/img/trans.png)
[英]React useEffect hook with warning react-hooks/exhaustive-deps
[英]React useEffect Hook when only one of the effect's deps changes, but not the others
我有一个使用 Hooks 的功能组件:
function Component(props) {
const [ items, setItems ] = useState([]);
// In a callback Hook to prevent unnecessary re-renders
const handleFetchItems = useCallback(() => {
fetchItemsFromApi().then(setItems);
}, []);
// Fetch items on mount
useEffect(() => {
handleFetchItems();
}, []);
// I want this effect to run only when 'props.itemId' changes,
// not when 'items' changes
useEffect(() => {
if (items) {
const item = items.find(item => item.id === props.itemId);
console.log("Item changed to " item.name);
}
}, [ items, props.itemId ])
// Clicking the button should NOT log anything to console
return (
<Button onClick={handleFetchItems}>Fetch items</Button>
);
}
该组件在挂载时获取一些items
并将它们保存到 state。
该组件接收一个itemId
道具(来自 React Router)。
每当props.itemId
更改时,我希望它触发效果,在这种情况下将其记录到控制台。
问题是,由于效果也依赖于items
,所以效果也会在items
发生变化时运行,例如当通过按下按钮重新获取items
时。
这可以通过将先前的props.itemId
存储在单独的 state 变量中并比较两者来解决,但这似乎是一种 hack 并添加了样板。 使用 Component 类可以通过比较componentDidUpdate
中的当前和以前的 props 来解决,但是使用功能组件是不可能的,这是使用 Hooks 的要求。
仅当其中一个参数发生变化时,触发依赖于多个参数的效果的最佳方法是什么?
PS。 Hooks 是一种新事物,我认为我们都在尽最大努力弄清楚如何正确使用它们,所以如果我的思考方式对你来说似乎是错误的或尴尬的,请指出。
我有一个使用Hooks的功能组件:
function Component(props) {
const [ items, setItems ] = useState([]);
// In a callback Hook to prevent unnecessary re-renders
const handleFetchItems = useCallback(() => {
fetchItemsFromApi().then(setItems);
}, []);
// Fetch items on mount
useEffect(() => {
handleFetchItems();
}, []);
// I want this effect to run only when 'props.itemId' changes,
// not when 'items' changes
useEffect(() => {
if (items) {
const item = items.find(item => item.id === props.itemId);
console.log("Item changed to " item.name);
}
}, [ items, props.itemId ])
// Clicking the button should NOT log anything to console
return (
<Button onClick={handleFetchItems}>Fetch items</Button>
);
}
该组件会获取安装中的某些items
并将其保存到状态。
该组件接收一个itemId
(来自React Router)。
每当props.itemId
更改时,我都希望它触发一个效果,在这种情况下,将其记录到控制台。
问题是,既然效果也依赖于items
,效果也将运行时items
的变化,例如当items
是重新获取的按下按钮。
可以通过将先前的props.itemId
存储在单独的状态变量中并进行比较来解决此问题,但这似乎很简单,并且增加了样板。 通过使用Component类,可以通过比较componentDidUpdate
当前和先前的道具来解决,但是使用功能组件是不可能的,这是使用Hooks的要求。
仅当其中一个参数发生更改时,触发取决于多个参数的效果的最佳方法是什么?
PS。 挂钩是一种新事物,我想我们所有人都在尽力找出如何正确使用挂钩的功能,因此,如果我的想法对您来说似乎是错误的或尴尬的,请指出。
我有一个使用Hooks的功能组件:
function Component(props) {
const [ items, setItems ] = useState([]);
// In a callback Hook to prevent unnecessary re-renders
const handleFetchItems = useCallback(() => {
fetchItemsFromApi().then(setItems);
}, []);
// Fetch items on mount
useEffect(() => {
handleFetchItems();
}, []);
// I want this effect to run only when 'props.itemId' changes,
// not when 'items' changes
useEffect(() => {
if (items) {
const item = items.find(item => item.id === props.itemId);
console.log("Item changed to " item.name);
}
}, [ items, props.itemId ])
// Clicking the button should NOT log anything to console
return (
<Button onClick={handleFetchItems}>Fetch items</Button>
);
}
该组件会获取安装中的某些items
并将其保存到状态。
该组件接收一个itemId
(来自React Router)。
每当props.itemId
更改时,我都希望它触发一个效果,在这种情况下,将其记录到控制台。
问题是,既然效果也依赖于items
,效果也将运行时items
的变化,例如当items
是重新获取的按下按钮。
可以通过将先前的props.itemId
存储在单独的状态变量中并进行比较来解决此问题,但这似乎很简单,并且增加了样板。 通过使用Component类,可以通过比较componentDidUpdate
当前和先前的道具来解决,但是使用功能组件是不可能的,这是使用Hooks的要求。
仅当其中一个参数发生更改时,触发取决于多个参数的效果的最佳方法是什么?
PS。 挂钩是一种新事物,我想我们所有人都在尽力找出如何正确使用挂钩的功能,因此,如果我的想法对您来说似乎是错误的或尴尬的,请指出。
我有一个使用Hooks的功能组件:
function Component(props) {
const [ items, setItems ] = useState([]);
// In a callback Hook to prevent unnecessary re-renders
const handleFetchItems = useCallback(() => {
fetchItemsFromApi().then(setItems);
}, []);
// Fetch items on mount
useEffect(() => {
handleFetchItems();
}, []);
// I want this effect to run only when 'props.itemId' changes,
// not when 'items' changes
useEffect(() => {
if (items) {
const item = items.find(item => item.id === props.itemId);
console.log("Item changed to " item.name);
}
}, [ items, props.itemId ])
// Clicking the button should NOT log anything to console
return (
<Button onClick={handleFetchItems}>Fetch items</Button>
);
}
该组件会获取安装中的某些items
并将其保存到状态。
该组件接收一个itemId
(来自React Router)。
每当props.itemId
更改时,我都希望它触发一个效果,在这种情况下,将其记录到控制台。
问题是,既然效果也依赖于items
,效果也将运行时items
的变化,例如当items
是重新获取的按下按钮。
可以通过将先前的props.itemId
存储在单独的状态变量中并进行比较来解决此问题,但这似乎很简单,并且增加了样板。 通过使用Component类,可以通过比较componentDidUpdate
当前和先前的道具来解决,但是使用功能组件是不可能的,这是使用Hooks的要求。
仅当其中一个参数发生更改时,触发取决于多个参数的效果的最佳方法是什么?
PS。 挂钩是一种新事物,我想我们所有人都在尽力找出如何正确使用挂钩的功能,因此,如果我的想法对您来说似乎是错误的或尴尬的,请指出。
我有一个使用Hooks的功能组件:
function Component(props) {
const [ items, setItems ] = useState([]);
// In a callback Hook to prevent unnecessary re-renders
const handleFetchItems = useCallback(() => {
fetchItemsFromApi().then(setItems);
}, []);
// Fetch items on mount
useEffect(() => {
handleFetchItems();
}, []);
// I want this effect to run only when 'props.itemId' changes,
// not when 'items' changes
useEffect(() => {
if (items) {
const item = items.find(item => item.id === props.itemId);
console.log("Item changed to " item.name);
}
}, [ items, props.itemId ])
// Clicking the button should NOT log anything to console
return (
<Button onClick={handleFetchItems}>Fetch items</Button>
);
}
该组件会获取安装中的某些items
并将其保存到状态。
该组件接收一个itemId
(来自React Router)。
每当props.itemId
更改时,我都希望它触发一个效果,在这种情况下,将其记录到控制台。
问题是,既然效果也依赖于items
,效果也将运行时items
的变化,例如当items
是重新获取的按下按钮。
可以通过将先前的props.itemId
存储在单独的状态变量中并进行比较来解决此问题,但这似乎很简单,并且增加了样板。 通过使用Component类,可以通过比较componentDidUpdate
当前和先前的道具来解决,但是使用功能组件是不可能的,这是使用Hooks的要求。
仅当其中一个参数发生更改时,触发取决于多个参数的效果的最佳方法是什么?
PS。 挂钩是一种新事物,我想我们所有人都在尽力找出如何正确使用挂钩的功能,因此,如果我的想法对您来说似乎是错误的或尴尬的,请指出。
我有一个使用Hooks的功能组件:
function Component(props) {
const [ items, setItems ] = useState([]);
// In a callback Hook to prevent unnecessary re-renders
const handleFetchItems = useCallback(() => {
fetchItemsFromApi().then(setItems);
}, []);
// Fetch items on mount
useEffect(() => {
handleFetchItems();
}, []);
// I want this effect to run only when 'props.itemId' changes,
// not when 'items' changes
useEffect(() => {
if (items) {
const item = items.find(item => item.id === props.itemId);
console.log("Item changed to " item.name);
}
}, [ items, props.itemId ])
// Clicking the button should NOT log anything to console
return (
<Button onClick={handleFetchItems}>Fetch items</Button>
);
}
该组件会获取安装中的某些items
并将其保存到状态。
该组件接收一个itemId
(来自React Router)。
每当props.itemId
更改时,我都希望它触发一个效果,在这种情况下,将其记录到控制台。
问题是,既然效果也依赖于items
,效果也将运行时items
的变化,例如当items
是重新获取的按下按钮。
可以通过将先前的props.itemId
存储在单独的状态变量中并进行比较来解决此问题,但这似乎很简单,并且增加了样板。 通过使用Component类,可以通过比较componentDidUpdate
当前和先前的道具来解决,但是使用功能组件是不可能的,这是使用Hooks的要求。
仅当其中一个参数发生更改时,触发取决于多个参数的效果的最佳方法是什么?
PS。 挂钩是一种新事物,我想我们所有人都在尽力找出如何正确使用挂钩的功能,因此,如果我的想法对您来说似乎是错误的或尴尬的,请指出。
我有一个使用Hooks的功能组件:
function Component(props) {
const [ items, setItems ] = useState([]);
// In a callback Hook to prevent unnecessary re-renders
const handleFetchItems = useCallback(() => {
fetchItemsFromApi().then(setItems);
}, []);
// Fetch items on mount
useEffect(() => {
handleFetchItems();
}, []);
// I want this effect to run only when 'props.itemId' changes,
// not when 'items' changes
useEffect(() => {
if (items) {
const item = items.find(item => item.id === props.itemId);
console.log("Item changed to " item.name);
}
}, [ items, props.itemId ])
// Clicking the button should NOT log anything to console
return (
<Button onClick={handleFetchItems}>Fetch items</Button>
);
}
该组件会获取安装中的某些items
并将其保存到状态。
该组件接收一个itemId
(来自React Router)。
每当props.itemId
更改时,我都希望它触发一个效果,在这种情况下,将其记录到控制台。
问题是,既然效果也依赖于items
,效果也将运行时items
的变化,例如当items
是重新获取的按下按钮。
可以通过将先前的props.itemId
存储在单独的状态变量中并进行比较来解决此问题,但这似乎很简单,并且增加了样板。 通过使用Component类,可以通过比较componentDidUpdate
当前和先前的道具来解决,但是使用功能组件是不可能的,这是使用Hooks的要求。
仅当其中一个参数发生更改时,触发取决于多个参数的效果的最佳方法是什么?
PS。 挂钩是一种新事物,我想我们所有人都在尽力找出如何正确使用挂钩的功能,因此,如果我的想法对您来说似乎是错误的或尴尬的,请指出。
基于前面的答案,并受到react-use 的 useCustomCompareEffect implementation 的启发,我继续编写useGranularEffect
钩子来解决类似的问题:
// I want this effect to run only when 'props.itemId' changes,
// not when 'items' changes
useGranularEffect(() => {
if (items) {
const item = items.find(item => item.id === props.itemId);
console.log("Item changed to " item.name);
}
}, [ items ], [ props.itemId ])
实现为(打字稿):
export const useGranularEffect = (
effect: EffectCallback,
primaryDeps: DependencyList,
secondaryDeps: DependencyList
) => {
const ref = useRef<DependencyList>();
if (!ref.current || !primaryDeps.every((w, i) => Object.is(w, ref.current[i]))) {
ref.current = [...primaryDeps, ...secondaryDeps];
}
// eslint-disable-next-line react-hooks/exhaustive-deps
return useEffect(effect, ref.current);
};
的签名useGranularEffect
相同useEffect
,除了依赖的名单已分成两个:
在我看来,它使仅在某些依赖项更改时才运行效果的情况更易于阅读。
笔记:
useGranularEffect
实现中的useGranularEffect
警告是安全的,因为effect
不是实际的依赖项(它是 effect 函数本身)并且ref.current
包含所有依赖项的列表(主要 + 次要, ref.current
无法猜测)useEffect
的行为一致,但可以随意使用您自己的比较函数,或者更好的是添加一个比较器作为参数我知道这是一个老问题,但仍然值得回答。 特效在今年受到了很多关注,现在你应该/不应该用特效做什么比当时更清楚了。 仍处于测试阶段的新 React 文档详细涵盖了该主题。
因此,在回答“如何”之前,您必须首先回答“为什么”,因为实际上只有在某些依赖项发生变化时才运行效果的真正用例并不多。
“您可能不需要效果”实际上是页面的标题,表明您可能出于错误的原因使用了效果。 你给我们的例子实际上是在' 当道具改变时调整一些 state '
您的代码可以简单地重写而没有第二种效果:
function Component(props) {
const [ items, setItems ] = useState([]);
// In a callback Hook to prevent unnecessary re-renders
const handleFetchItems = useCallback(() => {
fetchItemsFromApi().then(setItems);
}, []);
// Fetch items on mount
useEffect(() => {
handleFetchItems();
}, [handleFetchItems]);
// Find the item that matches in the list
const item = items.find(item => item.id === props.itemId);
return (
<Button onClick={handleFetchItems}>Fetch items</Button>
);
}
实际上,您不需要一个效果来识别列表中的项目。 React 已经在items
更改时重新渲染,因此可以直接在组件中重新计算item
。
注意:如果您真的想在项目更改时登录到控制台,您仍然可以将此代码添加到您的组件中:
useEffect(() => {
console.log("Item changed to ", item?.name);
}, [item])
请注意item
是 effect 的唯一依赖项,因此它仅在item
更改时运行。 但这又不是一个效果的好用例。
相反,如果您想对应用程序中发生的事件做出响应,您应该这样做
是的,“ 从效果中分离事件”也是新 React 文档中页面的标题,在您给我们的示例中,您希望在item
更改时写入控制台。 但我并不天真,我知道你可能想在练习中做更多的事情。
如果你想在其他事情发生变化时做某事,那么你可以使用新的useEvent
钩子为它创建一个事件处理程序(或者至少使用它的 polyfill,因为它还没有发布)。 这里有问题的事件是itemId
道具的变化。
然后,您可以将第二个效果更改为:
// Handle a change of item ID
const onItemIdChange = useEvent((itemId) => {
const item = items?.find(item => item.id === itemId);
console.log("Item changed to " item.name);
});
// Trigger onItemIdChange when item ID changes
useEffect(() => {
onItemIdChange(props.itemId);
}, [props.itemId]);
这种方法的优点是useEvent
返回一个稳定的function,所以你不需要将它作为依赖添加到你的效果中(它永远不会改变)。 此外,传递给useEvent
的 function 没有依赖数组:它可以访问组件内的所有属性、状态或变量,并且它始终是新鲜的(没有陈旧的值)。
我仍然认为根据你给我们的例子,你真的不需要效果。 但是对于其他真实情况,至少您现在知道如何从效果中提取事件,因此您可以避免破解依赖数组。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.