简体   繁体   English

React hooks:当 state 本身是依赖项时,如何在 useEffect 中更新 state ?

[英]React hooks : how can I update a state within useEffect when the state itself is the dependency?

I know there already are already some related questions, like How can React useEffect watch and update state?我知道已经有一些相关的问题,比如React useEffect 如何监视和更新 state? , but still, I don't get it totally. ,但是,我还是不完全明白。

Let's say I set an index state based on a prop;假设我根据道具设置了index state ; and I need to sanitize that value anytime it is set.并且我需要在设置该值的任何时候对其进行清理。

<MyComponent index={4}/>

This is how I attempted to do it:这就是我尝试这样做的方式:

useEffect(() => {
  setIndex(props.index);
}, [props.index]);

useEffect(() => {
  const sanitized = sanitizeIndex(index);
  setIndex(sanitized);
},[index])

const sanitizeIndex = index => {
    //check that index exists in array...
    //use fallback if not...
    //etc.
    return index
}

It does not work (infinite loop), since the state is watched and updated by the second useEffect() .它不起作用(无限循环),因为 state 由第二个useEffect()监视更新。

Of course, I could avoid this by calling sanitizeIndex() on the prop, so I only need a single instance of useEffect() :当然,我可以通过在 prop 上调用sanitizeIndex()来避免这种情况,所以我只需要一个useEffect()实例:

useEffect(() => {
  setIndex(sanitizeIndex(props.index));
}, [props.index]);

Thing is, I call setIndex plenty of times in my code, and I fear to miss using sanitizeIndex .问题是,我在代码中多次调用setIndex ,我担心会错过使用sanitizeIndex

Is there another way to "catch" and update a state value being set?是否有另一种方法可以“捕获”并更新正在设置的 state 值?

Thanks !谢谢 !

So think of useEffect like an event listener in javascript.所以将 useEffect 想象成 javascript 中的事件监听器。 It's not the same thing, but think of it like that.这不是一回事,但这样想。 The event or "what's being watched", in this case, you've asked it to watch props.index.事件或“正在观看的内容”,在这种情况下,您已要求它观看 props.index。 It will run that function inside the useEffect every time anything in the dependency array (props.index - in your case) changes.每当依赖数组(props.index - 在您的情况下)中的任何内容发生更改时,它都会在 useEffect 中运行 function 。 So what's happening here is you're updating props.index every time props.index changes.所以这里发生的事情是每次 props.index 更改时您都在更新 props.index。 This is your infinite loop.这是你的无限循环。

Couple things here, create a copy of props.index as something ie.在这里结合一些东西,创建一个 props.index 的副本,即。
const [something, setSomething = useState(props.index);
(I won't get into destructuring, but worth looking that up too) You don't want to manipulate your props directly the way you are doing. (我不会涉及解构,但也值得一试)你不想像你正在做的那样直接操纵你的道具。

This solves that, as well as gives you the correct way to look at your useEffect.这解决了这个问题,并为您提供了查看 useEffect 的正确方法。 Since you want to update something whenever that prop changes, you could leave props.index (again look up destructuring) in your dependency array, and change the useEffect to:由于您想在该道具更改时更新某些内容,因此您可以将 props.index (再次查找解构)留在您的依赖数组中,并将 useEffect 更改为:

const [something, setSomething] = useState(props.index);

useEffect(() => {
  setSomething(props.index);
}, [props.index]);

As another pointed out, this is difficult without knowing exactly what you're doing, but this is kind of an overview which hopefully helps you understand what is going on here and why you're getting a loop here.正如另一位指出的那样,如果不确切知道您在做什么,这很困难,但这是一种概述,希望能帮助您了解这里发生了什么以及为什么在这里出现循环。

You mentioned you fear missing out on sanitizing, then you should not be using setIndex directly.你提到你害怕错过消毒,那么你不应该直接使用 setIndex 。 Instead, you can create a new function to santize and set the index.相反,您可以创建一个新的 function 来清理和设置索引。

useEffect(() => {
  setSanitizeIndex(props.index);
}, [props.index]);

const setSanitizeIndex = (value) => {
    const sanitizeIndex = sanitizeIndex(value);
    setIndex(sanitizeIndex)
}

With that, you should not be calling setIndex any more in your codes, but only call setSanitizeIndex .有了这个,你不应该在你的代码中再调用setSanitizeIndex setIndex

This seems like a good case for a custom hook .这对于自定义钩子来说似乎是一个很好的案例。 Here's an example of how to implement one for your case (given the information currently provided in your question), including comments about how/why:以下是如何为您的案例实施一个示例(鉴于您的问题中当前提供的信息),包括有关如何/为什么的评论:

Be sure to read the documentation foruseCallback if you are not already familiar with it.如果您还不熟悉useCallback ,请务必阅读文档。 It's especially important to understand how to use the dependency array ( link 1 , link 2 ) when using hooks which utilize it (like useCallback and useEffect ).在使用使用它的钩子(如useCallbackuseEffect )时,了解如何使用依赖数组( 链接 1链接 2 )尤为重要。

 <div id="root"></div><script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script><script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.development.js"></script><script src="https://unpkg.com/@babel/standalone@7.16.12/babel.min.js"></script> <script type="text/babel" data-type="module" data-presets="env,react"> const {useCallback, useEffect, useState} = React; /** * You didn't show exactly how you are sanitizing, so I'm using this function * instead. It will simply make sure the input number is always even by * adding 1 to it if it's odd. */ function makeEven (n) { return n % 2 === 0? n: n + 1; } function useSanitizedIndex (sanitizeIndex, unsanitizedIndex) { const [index, setIndex] = useState(sanitizeIndex(unsanitizedIndex)); // Like setIndex, but also sanitizes const setSanitizedIndex = useCallback( (unsanitizedIndex) => setIndex(sanitizeIndex(unsanitizedIndex)), [sanitizeIndex, setIndex], ); // Update state if arguments change useEffect( () => setSanitizedIndex(unsanitizedIndex), [setSanitizedIndex, unsanitizedIndex], ); return [index, setSanitizedIndex]; } function IndexComponent (props) { // useCallback memoizes the function so that it's not recreated on every // render. This also prevents the custom hook from looping infinintely const sanitizeIndex = useCallback((unsanitizedIndex) => { // Whatever you actually do to sanitize the index goes in here, // but I'll just use the makeEven function for this example return makeEven(unsanitizedIndex); // If you use other variables in this function which are defined in this // component (eg you mentioned an array state of some kind), you'll need // to include them in the dependency array below: }, []); // Now simply use the sanitized index where you need it, // and the setter will sanitize for you when setting it (like in the // click handler in the button below) const [index, setSanitizedIndex] = useSanitizedIndex(sanitizeIndex, props.index); return ( <div> <div>Sanitized index (will always be even): {index}</div> <button onClick={() => setSanitizedIndex(5)}>Set to 5</button> </div> ); } function Example () { const [count, setCount] = useState(0); return ( <div> <div>Count: {count}</div> <button onClick={() => setCount(n => n + 1)}>Increment</button> <IndexComponent index={count} /> </div> ); } ReactDOM.render(<Example />, document.getElementById('root')); </script>

One potential solution for this that I have done in the past with a similar issue is to indirectly trigger the useEffect.我过去针对类似问题所做的一个潜在解决方案是间接触发 useEffect。 Create a dummy state that does not relate to the state being updated and update the dummy state whenever you want the effect to be triggered.创建一个与正在更新的 state 无关的虚拟 state,并在您希望触发效果时更新虚拟 state。

const [dummyState, setDummyState] = useState(0)
useEffect(() => {
  setIndex(props.index);
}, [props.index]);

useEffect(() => {
  const sanitized = sanitizeIndex(index);
  setIndex(sanitized);
},[dummyState])

const sanitizeIndex = index => {
    //check that index exists in array...
    //use fallback if not...
    //etc.
    return index
}
return (
    //update the state you want to update then update the dummy state to 
    //trigger the useEffect
    <button onClick={() =>{setIndex(123);
                           setDummyState(dummyState++);}/>
)

I think the accepted answers solution is a lot less janky but in more complex situations this might be your easiest and most easy-to-understand solution我认为接受的答案解决方案不那么笨拙,但在更复杂的情况下,这可能是您最简单和最容易理解的解决方案

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 React挂钩:使用useEffect时无法获取状态更新 - React hooks: Not getting the state update when using useEffect 如何使用React Hooks更新依赖于其他状态的状态 - How to update state that has dependency on other state with React Hooks 如何使用 socket.io-client 在 useEffect 挂钩中更新 React state - How update React state in useEffect hooks with socket.io-client 在 React 中,当它是 useEffect 的依赖项而不触发另一个 API 调用时,我如何将 append 值转换为功能 state? - In React how can I append values to functional state when it is a dependency of useEffect without triggering another API call? React 无法在 useEffect() 中使用钩子设置状态 - React can't set state with hooks in useEffect() 当我尝试更改 useEffect() 中的状态时,“无法对未安装的组件执行 React 状态更新” - "Can't perform a React state update on an unmounted component" when i try to change state in useEffect() 如何在 React 中通过 useEffect 更新 state? - How to update state by useEffect in React? React Hooks - useEffect() 运行两次,因为依赖项的 state 未定义 - React Hooks - useEffect() runs twice because state of a dependency is undefined 如何更新 State React Hooks? - How to Update State React Hooks? 无法对未安装的组件执行 React state 更新(useEffect 反应挂钩) - Can't perform a React state update on an unmounted component (useEffect react hooks)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM