簡體   English   中英

React hooks:當 state 本身是依賴項時,如何在 useEffect 中更新 state ?

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

我知道已經有一些相關的問題,比如React useEffect 如何監視和更新 state? ,但是,我還是不完全明白。

假設我根據道具設置了index state ; 並且我需要在設置該值的任何時候對其進行清理。

<MyComponent index={4}/>

這就是我嘗試這樣做的方式:

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
}

它不起作用(無限循環),因為 state 由第二個useEffect()監視更新。

當然,我可以通過在 prop 上調用sanitizeIndex()來避免這種情況,所以我只需要一個useEffect()實例:

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

問題是,我在代碼中多次調用setIndex ,我擔心會錯過使用sanitizeIndex

是否有另一種方法可以“捕獲”並更新正在設置的 state 值?

謝謝 !

所以將 useEffect 想象成 javascript 中的事件監聽器。 這不是一回事,但這樣想。 事件或“正在觀看的內容”,在這種情況下,您已要求它觀看 props.index。 每當依賴數組(props.index - 在您的情況下)中的任何內容發生更改時,它都會在 useEffect 中運行 function 。 所以這里發生的事情是每次 props.index 更改時您都在更新 props.index。 這是你的無限循環。

在這里結合一些東西,創建一個 props.index 的副本,即。
const [something, setSomething = useState(props.index);
(我不會涉及解構,但也值得一試)你不想像你正在做的那樣直接操縱你的道具。

這解決了這個問題,並為您提供了查看 useEffect 的正確方法。 由於您想在該道具更改時更新某些內容,因此您可以將 props.index (再次查找解構)留在您的依賴數組中,並將 useEffect 更改為:

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

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

正如另一位指出的那樣,如果不確切知道您在做什么,這很困難,但這是一種概述,希望能幫助您了解這里發生了什么以及為什么在這里出現循環。

你提到你害怕錯過消毒,那么你不應該直接使用 setIndex 。 相反,您可以創建一個新的 function 來清理和設置索引。

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

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

有了這個,你不應該在你的代碼中再調用setSanitizeIndex setIndex

這對於自定義鈎子來說似乎是一個很好的案例。 以下是如何為您的案例實施一個示例(鑒於您的問題中當前提供的信息),包括有關如何/為什么的評論:

如果您還不熟悉useCallback ,請務必閱讀文檔。 在使用使用它的鈎子(如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>

我過去針對類似問題所做的一個潛在解決方案是間接觸發 useEffect。 創建一個與正在更新的 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++);}/>
)

我認為接受的答案解決方案不那么笨拙,但在更復雜的情況下,這可能是您最簡單和最容易理解的解決方案

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM