【问题标题】:The useState set method is not reflecting a change immediatelyuseState set 方法没有立即反映变化
【发布时间】:2022-10-10 01:09:55
【问题描述】:

我正在尝试学习钩子,useState 方法让我感到困惑。我正在以数组的形式为状态分配一个初始值。 useState 中的 set 方法对我不起作用,无论是否使用扩展语法。

我在另一台我正在调用的 PC 上制作了一个 API,并获取了我想要设置为状态的数据。

这是我的代码:

<div id="root"></div>

<script type="text/babel" defer>
// import React, { useState, useEffect } from "react";
// import ReactDOM from "react-dom";
const { useState, useEffect } = React; // web-browser variant

const StateSelector = () => {
  const initialValue = [
    {
      category: "",
      photo: "",
      description: "",
      id: 0,
      name: "",
      rating: 0
    }
  ];

  const [movies, setMovies] = useState(initialValue);

  useEffect(() => {
    (async function() {
      try {
        // const response = await fetch("http://192.168.1.164:5000/movies/display");
        // const json = await response.json();
        // const result = json.data.result;
        const result = [
          {
            category: "cat1",
            description: "desc1",
            id: "1546514491119",
            name: "randomname2",
            photo: null,
            rating: "3"
          },
          {
            category: "cat2",
            description: "desc1",
            id: "1546837819818",
            name: "randomname1",
            rating: "5"
          }
        ];
        console.log("result =", result);
        setMovies(result);
        console.log("movies =", movies);
      } catch (e) {
        console.error(e);
      }
    })();
  }, []);

  return <p>hello</p>;
};

const rootElement = document.getElementById("root");
ReactDOM.render(<StateSelector />, rootElement);
</script>

<script src="https://unpkg.com/@babel/standalone@7/babel.min.js"></script>
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>

setMovies(result)setMovies(...result) 都不起作用。

我希望 result 变量被推入 movies 数组。

【问题讨论】:

  • 您是否能够看到将 console.log("movies =", movies); 移出 useEffect 挂钩的更改?
  • 不过问得好

标签: javascript reactjs react-hooks


【解决方案1】:

许多like .setState() in class components通过扩展React.ComponentReact.PureComponent创建,使用useState钩子提供的updater更新状态也是异步的,不会立即反映。

此外,这里的主要问题不仅仅是异步性质,而是基于函数当前闭包使用状态值的事实,状态更新将反映在下一次重新渲染中,现有闭包不受影响,但新的那些被创建.现在在当前状态下,钩子中的值是由现有闭包获取的,当重新渲染发生时,闭包会根据函数是否重新创建来更新。

即使你添加一个 setTimeout 函数,尽管超时将在重新渲染发生的一段时间后运行,setTimeout 仍将使用其先前关闭的值而不是更新的值。

setMovies(result);
console.log(movies) // movies here will not be updated

如果要对状态更新执行操作,则需要使用 useEffect 挂钩,就像在类组件中使用 componentDidUpdate 一样,因为 useState 返回的 setter 没有回调模式

useEffect(() => {
    // action on update of movies
}, [movies]);

就更新状态的语法而言,setMovies(result) 会将状态中先前的 movies 值替换为异步请求中可用的值。

但是,如果您想将响应与以前存在的值合并,则必须使用状态更新的回调语法以及正确使用扩展语法,例如

setMovies(prevMovies => ([...prevMovies, ...result]));

【讨论】:

  • 嗨,在表单提交处理程序中调用 useState 怎么样?我正在验证一个复杂的表单,我在 submitHandler useState 挂钩中调用,不幸的是更改不是立即的!
  • useEffect 可能不是最好的解决方案,因为它不支持异步调用。因此,如果我们想对movies 状态更改进行一些异步验证,我们无法控制它。
  • 请注意,虽然建议非常好,但可以改进对原因的解释——与the updater provided by useState hook是否异步无关,不像this.state,如果this.setState是同步的,它可能会发生变异,即使useState 提供了同步功能,const movies 周围的闭包也会保持不变 - 请参阅我的回答中的示例
  • setMovies(prevMovies =&gt; ([...prevMovies, ...result])); 为我工作
  • 它记录了错误的结果,因为您记录的是stale closure,而不是因为设置器是异步的。如果异步是问题,那么您可以在超时后登录,但您可以设置一个小时的超时并仍然记录错误的结果,因为异步不是导致问题的原因。
【解决方案2】:

previous answer的其他详细信息:

而 React 的setState异步(类和钩子),很容易用这个事实来解释观察到的行为,这不是原因它发生了。

TLDR:原因是 closure 范围围绕着不可变的 const 值。


解决方案:

  • 读取渲染函数中的值(不在嵌套函数中):

      useEffect(() => { setMovies(result) }, [])
      console.log(movies)
    
  • 将变量添加到依赖项中(并使用 react-hooks/exhaustive-deps eslint 规则):

      useEffect(() => { setMovies(result) }, [])
      useEffect(() => { console.log(movies) }, [movies])
    
  • 使用临时变量:

      useEffect(() => {
        const newMovies = result
        console.log(newMovies)
        setMovies(newMovies)
      }, [])
    
  • 使用可变引用(如果我们不需要状态并且只想记住值 - 更新引用不会触发重新渲染):

      const moviesRef = useRef(initialValue)
      useEffect(() => {
        moviesRef.current = result
        console.log(moviesRef.current)
      }, [])
    

解释为什么会这样:

如果异步是唯一的原因,await setState() 是可能的。

然而,propsstate都是assumed to be unchanging during 1 render

this.state 视为不可变的。

使用钩子,可以通过使用增强此假设常数值使用 const 关键字:

const [state, setState] = useState('initial')

该值在 2 个渲染之间可能不同,但在渲染本身和任何 closures 中保持不变(即使在渲染完成后仍然存在的函数,例如 useEffect,事件处理程序,在任何 Promise 或 setTimeout 中)。

考虑跟随假的,但是同步, 类似 React 的实现:

// sync implementation:

let internalState
let renderAgain

const setState = (updateFn) => {
  internalState = updateFn(internalState)
  renderAgain()
}

const useState = (defaultState) => {
  if (!internalState) {
    internalState = defaultState
  }
  return [internalState, setState]
}

const render = (component, node) => {
  const {html, handleClick} = component()
  node.innerHTML = html
  renderAgain = () => render(component, node)
  return handleClick
}

// test:

const MyComponent = () => {
  const [x, setX] = useState(1)
  console.log('in render:', x) // ✅
  
  const handleClick = () => {
    setX(current => current + 1)
    console.log('in handler/effect/Promise/setTimeout:', x) // ❌ NOT updated
  }
  
  return {
    html: `<button>${x}</button>`,
    handleClick
  }
}

const triggerClick = render(MyComponent, document.getElementById('root'))
triggerClick()
triggerClick()
triggerClick()
&lt;div id="root"&gt;&lt;/div&gt;

【讨论】:

  • @AlJoslin 乍一看,这似乎是一个单独的问题,即使它可能是由闭包范围引起的。如果您有具体问题,请使用代码示例和所有内容创建一个新的 stackoverflow 问题...
  • 实际上,我刚刚完成了对 useReducer 的重写,遵循 @kentcdobs 文章(下面的参考),它确实给了我一个可靠的结果,不会受到这些闭包问题的影响。 (参考:kentcdodds.com/blog/how-to-use-react-context-effectively
  • 由于某种原因,解决方案 2 不起作用。我收到回调,但该值仍然为空。 useEffect(() =&gt; { console.log(movies) }, [movies])这什么都不打印..
  • @ACV 解决方案 2 适用于原始问题。如果您需要解决不同的问题,YMMW,但我仍然 100% 确定引用的代码按照记录工作并且问题出在其他地方。
  • 所有这些解决方案都需要使用 useEffect。我的问题是我的“电影”等价物是我从上下文提供者那里得到的一个对象,可以被许多其他组件更改。我不想每次更改时都运行效果,因为我的效果不是 setMovies - 它是一个不同的函数,我只需要在对电影进行特定更改时调用 - 我在需要时看不到的更改是因为陈旧的上下文。
【解决方案3】:

我知道已经有很好的答案了。但我想给出另一个想法,如何使用我的模块react-useStateRef 解决同样的问题,并访问最新的“电影”状态。

正如您所了解的,通过使用 React 状态,您可以在每次状态更改时呈现页面。但是通过使用 React ref,你总是可以获得最新的值。

所以模块react-useStateRef 允许您一起使用状态和引用。它向后兼容React.useState,因此您只需替换import语句即可

const { useEffect } = React
import { useState } from 'react-usestateref'

  const [movies, setMovies] = useState(initialValue);

  useEffect(() => {
    (async function() {
      try {

        const result = [
          {
            id: "1546514491119",
          },
        ];
        console.log("result =", result);
        setMovies(result);
        console.log("movies =", movies.current); // will give you the latest results
      } catch (e) {
        console.error(e);
      }
    })();
  }, []);

更多信息:

【讨论】:

    【解决方案4】:

    我刚刚完成了对 useReducer 的重写,遵循 @kentcdobs 文章(下面的参考),它确实给了我一个可靠的结果,不会受到这些闭包问题的影响。

    见:https://kentcdodds.com/blog/how-to-use-react-context-effectively

    我将他的可读样板压缩到我喜欢的 DRYness 级别——阅读他的沙箱实现将向您展示它的实际工作原理。

    import React from 'react'
    
    // ref: https://kentcdodds.com/blog/how-to-use-react-context-effectively
    
    const ApplicationDispatch = React.createContext()
    const ApplicationContext = React.createContext()
    
    function stateReducer(state, action) {
      if (state.hasOwnProperty(action.type)) {
        return { ...state, [action.type]: state[action.type] = action.newValue };
      }
      throw new Error(`Unhandled action type: ${action.type}`);
    }
    
    const initialState = {
      keyCode: '',
      testCode: '',
      testMode: false,
      phoneNumber: '',
      resultCode: null,
      mobileInfo: '',
      configName: '',
      appConfig: {},
    };
    
    function DispatchProvider({ children }) {
      const [state, dispatch] = React.useReducer(stateReducer, initialState);
      return (
        <ApplicationDispatch.Provider value={dispatch}>
          <ApplicationContext.Provider value={state}>
            {children}
          </ApplicationContext.Provider>
        </ApplicationDispatch.Provider>
      )
    }
    
    function useDispatchable(stateName) {
      const context = React.useContext(ApplicationContext);
      const dispatch = React.useContext(ApplicationDispatch);
      return [context[stateName], newValue => dispatch({ type: stateName, newValue })];
    }
    
    function useKeyCode() { return useDispatchable('keyCode'); }
    function useTestCode() { return useDispatchable('testCode'); }
    function useTestMode() { return useDispatchable('testMode'); }
    function usePhoneNumber() { return useDispatchable('phoneNumber'); }
    function useResultCode() { return useDispatchable('resultCode'); }
    function useMobileInfo() { return useDispatchable('mobileInfo'); }
    function useConfigName() { return useDispatchable('configName'); }
    function useAppConfig() { return useDispatchable('appConfig'); }
    
    export {
      DispatchProvider,
      useKeyCode,
      useTestCode,
      useTestMode,
      usePhoneNumber,
      useResultCode,
      useMobileInfo,
      useConfigName,
      useAppConfig,
    }
    

    用法与此类似:

    import { useHistory } from "react-router-dom";
    
    // https://react-bootstrap.github.io/components/alerts
    import { Container, Row } from 'react-bootstrap';
    
    import { useAppConfig, useKeyCode, usePhoneNumber } from '../../ApplicationDispatchProvider';
    
    import { ControlSet } from '../../components/control-set';
    import { keypadClass } from '../../utils/style-utils';
    import { MaskedEntry } from '../../components/masked-entry';
    import { Messaging } from '../../components/messaging';
    import { SimpleKeypad, HandleKeyPress, ALT_ID } from '../../components/simple-keypad';
    
    export const AltIdPage = () => {
      const history = useHistory();
      const [keyCode, setKeyCode] = useKeyCode();
      const [phoneNumber, setPhoneNumber] = usePhoneNumber();
      const [appConfig, setAppConfig] = useAppConfig();
    
      const keyPressed = btn => {
        const maxLen = appConfig.phoneNumberEntry.entryLen;
        const newValue = HandleKeyPress(btn, phoneNumber).slice(0, maxLen);
        setPhoneNumber(newValue);
      }
    
      const doSubmit = () => {
        history.push('s');
      }
    
      const disableBtns = phoneNumber.length < appConfig.phoneNumberEntry.entryLen;
    
      return (
        <Container fluid className="text-center">
          <Row>
            <Messaging {...{ msgColors: appConfig.pageColors, msgLines: appConfig.entryMsgs.altIdMsgs }} />
          </Row>
          <Row>
            <MaskedEntry {...{ ...appConfig.phoneNumberEntry, entryColors: appConfig.pageColors, entryLine: phoneNumber }} />
          </Row>
          <Row>
            <SimpleKeypad {...{ keyboardName: ALT_ID, themeName: appConfig.keyTheme, keyPressed, styleClass: keypadClass }} />
          </Row>
          <Row>
            <ControlSet {...{ btnColors: appConfig.buttonColors, disabled: disableBtns, btns: [{ text: 'Submit', click: doSubmit }] }} />
          </Row>
        </Container>
      );
    };
    
    AltIdPage.propTypes = {};
    

    现在一切都在我所有的页面上无处不在

    【讨论】:

    • 我不认为这个答案在 OP 的上下文中特别有用。这个答案甚至没有使用 useState(),这是 OP 查询的核心。
    • 顺利的解决方案,但不是正在发生的事情的答案
    【解决方案5】:

    React 的 useEffect 有自己的状态/生命周期。它与状态的突变有关,直到效果被销毁后才会更新状态。

    只需在参数状态中传递一个参数或将其保留为黑色数组即可完美运行。

    React.useEffect(() => {
        console.log("effect");
        (async () => {
            try {
                let result = await fetch("/query/countries");
                const res = await result.json();
                let result1 = await fetch("/query/projects");
                const res1 = await result1.json();
                let result11 = await fetch("/query/regions");
                const res11 = await result11.json();
                setData({
                    countries: res,
                    projects: res1,
                    regions: res11
                });
            } catch {}
        })(data)
    }, [setData])
    # or use this
    useEffect(() => {
        (async () => {
            try {
                await Promise.all([
                    fetch("/query/countries").then((response) => response.json()),
                    fetch("/query/projects").then((response) => response.json()),
                    fetch("/query/regions").then((response) => response.json())
                ]).then(([country, project, region]) => {
                    // console.log(country, project, region);
                    setData({
                        countries: country,
                        projects: project,
                        regions: region
                    });
                })
            } catch {
                console.log("data fetch error")
            }
        })()
    }, [setData]);
    

    或者,您可以尝试 React.useRef() 在 React 钩子中进行即时更改。

    const movies = React.useRef(null);
    useEffect(() => {
    movies.current='values';
    console.log(movies.current)
    }, [])
    

    【讨论】:

    • 当您使用 Promise API 时,最后一个代码示例既不需要 async 也不需要 await 。这只需要在第一个
    【解决方案6】:

    关闭不是唯一的原因。

    基于useState的源代码(下面进行了简化)。在我看来,价值永远不会立即分配。

    当您调用setValue 时,会发生一个更新操作排队的情况。在计划开始后,只有当您到达下一个渲染时,这些更新操作才会应用于该状态。

    这意味着即使我们没有关闭问题,useState 的 React 版本也不会立即为您提供新值。在下一次渲染之前,新值甚至不存在。

      function useState(initialState) {
        let hook;
        ...
    
        let baseState = hook.memoizedState;
        if (hook.queue.pending) {
          let firstUpdate = hook.queue.pending.next;
    
          do {
            const action = firstUpdate.action;
            baseState = action(baseState);            // setValue HERE
            firstUpdate = firstUpdate.next;
          } while (firstUpdate !== hook.queue.pending);
    
          hook.queue.pending = null;
        }
        hook.memoizedState = baseState;
    
        return [baseState, dispatchAction.bind(null, hook.queue)];
      }
    
    function dispatchAction(queue, action) {
      const update = {
        action,
        next: null
      };
      if (queue.pending === null) {
        update.next = update;
      } else {
        update.next = queue.pending.next;
        queue.pending.next = update;
      }
      queue.pending = update;
    
      isMount = false;
      workInProgressHook = fiber.memoizedState;
      schedule();
    }
    
    

    还有一篇文章用类似的方式解释了上面的内容,https://dev.to/adamklein/we-don-t-know-how-react-state-hook-works-1lp8

    【讨论】:

      【解决方案7】:

      我也遇到了同样的问题。由于上面的其他答案已经澄清了这里的错误,即 useState 是异步的,您正试图使用​​ setState 之后的值。由于 setState 的异步性质,它不会在 console.log() 部分更新,它允许您执行进一步的代码,而值更新发生在后台。因此,您将获得以前的价值。当 setState 在后台完成时,它将更新该值,您将可以在下一次渲染时访问该值。

      如果有人有兴趣详细了解这一点。这是关于该主题的非常好的会议演讲。

      https://www.youtube.com/watch?v=8aGhZQkoFbQ

      【讨论】:

      • 清晰简单谢谢!
      【解决方案8】:

      我发现这很好。与其将状态(方法 1)定义为,例如,

      const initialValue = 1;
      
      const [state,setState] = useState(initialValue)
      
      

      尝试这种方法(方法 2),

      const [state = initialValue,setState] = useState()
      
      

      这在不使用 useEffect 的情况下解决了重新渲染问题,因为我们不关心它在这种情况下的内部关闭方法。

      P.S.:如果您担心在任何用例中使用旧状态,则需要使用带有 useEffect 的 useState,因为它需要具有该状态,因此在这种情况下应使用方法 1。

      【讨论】:

      • 这个答案没有用。关于重新渲染和捕获的闭包值,这种方法没有丝毫区别。唯一的区别是当状态值被故意设置为undefined时,在这种情况下你将再次获得initialValue。这只是一种令人困惑的做法,因为您可以将其设置为初始值而无需额外的步骤。
      • 方法 2 污染了全局空间。如前所述,方法 1 充其量是一种反模式。
      【解决方案9】:

      如果我们只需要更新状态,那么更好的方法是使用 push 方法。

      这是我的代码。我想在状态中存储来自 Firebase 的 URL。

      const [imageUrl, setImageUrl] = useState([]);
      const [reload, setReload] = useState(0);
      
      useEffect(() => {
          if (reload === 4) {
              downloadUrl1();
          }
      }, [reload]);
      
      
      const downloadUrl = async () => {
          setImages([]);
          try {
              for (let i = 0; i < images.length; i++) {
                  let url = await storage().ref(urls[i].path).getDownloadURL();
                  imageUrl.push(url);
                  setImageUrl([...imageUrl]);
      
                  console.log(url, 'check', urls.length, 'length', imageUrl.length);
              }
          }
          catch (e) {
              console.log(e);
          }
      };
      
      const handleSubmit = async () => {
          setReload(4);
          await downloadUrl();
          console.log(imageUrl);
          console.log('post submitted');
      };
      

      此代码用于将 URL 作为数组置于状态中。这也可能对您有用。

      【讨论】:

      • .push 改变当前状态,即 bad practice in React。这是update a state array的正确方式。
      • 在循环中调用 setImageUrl 是另一种不好的做法,它会在每次调用时触发一个新的渲染,因为它在异步调用时没有被批处理(在 React 的生命周期之外)。正确的方法是构建新数组,然后只调用一次setImageUrl
      • 另外,使用await in a loop like that is inefficientPromise.all之类的东西会改善这一点。
      【解决方案10】:

      我希望状态的更新值立即将其保存在数据库中,我遇到了这个问题。调用更新程序只是为了获取最新值。

      const [pin, setPin] = useState(0);
      setPin(pin ? 0 : 1);
      setPin((state) => {
        console.log(state); // 1
        //Here I called the DB method and passed the state as param in mehtod
        return state;
      });
      

      【讨论】:

        【解决方案11】:

        使用我的库中的自定义挂钩,您可以等待状态值更新:

        1. useAsyncWatcher(...values):watcherFn(peekPrevValue: boolean)=&gt;Promise - 是 useEffect 的承诺包装器,它可以等待更新并返回一个新值,如果可选的 peekPrevValue 参数设置为 true,则可能返回前一个值。

          (Live Demo)

              import React, { useState, useEffect, useCallback } from "react";
              import { useAsyncWatcher } from "use-async-effect2";
              
              function TestComponent(props) {
                const [counter, setCounter] = useState(0);
                const [text, setText] = useState("");
              
                const textWatcher = useAsyncWatcher(text);
              
                useEffect(() => {
                  setText(`Counter: ${counter}`);
                }, [counter]);
              
                const inc = useCallback(() => {
                  (async () => {
                    await new Promise((resolve) => setTimeout(resolve, 1000));
                    setCounter((counter) => counter + 1);
                    const updatedText = await textWatcher();
                    console.log(updatedText);
                  })();
                }, []);
              
                return (
                  <div className="component">
                    <div className="caption">useAsyncEffect demo</div>
                    <div>{counter}</div>
                    <button onClick={inc}>Inc counter</button>
                  </div>
                );
              }
              
              export default TestComponent;
          
          1. useAsyncDeepState 是一个深度状态实现(类似于 this.setState (patchObject)),其设置器可以返回一个与内部效果同步的承诺。如果不带参数调用 setter,它不会更改状态值,而只是订阅状态更新。在这种情况下,您可以从组件内部的任何位置获取状态值,因为函数闭包不再是障碍。

          (Live Demo)

          import React, { useCallback, useEffect } from "react";
          import { useAsyncDeepState } from "use-async-effect2";
          
          function TestComponent(props) {
            const [state, setState] = useAsyncDeepState({
              counter: 0,
              computedCounter: 0
            });
          
            useEffect(() => {
              setState(({ counter }) => ({
                computedCounter: counter * 2
              }));
            }, [state.counter]);
          
            const inc = useCallback(() => {
              (async () => {
                await new Promise((resolve) => setTimeout(resolve, 1000));
                await setState(({ counter }) => ({ counter: counter + 1 }));
                console.log("computedCounter=", state.computedCounter);
              })();
            });
          
            return (
              <div className="component">
                <div className="caption">useAsyncDeepState demo</div>
                <div>state.counter : {state.counter}</div>
                <div>state.computedCounter : {state.computedCounter}</div>
                <button onClick={() => inc()}>Inc counter</button>
              </div>
            );
          }
          

        【讨论】:

          【解决方案12】:
          var [state,setState]=useState(defaultValue)
          
          useEffect(()=>{
             var updatedState
             setState(currentState=>{    // Do not change the state by get the updated state
                updateState=currentState
                return currentState
             })
             alert(updateState) // the current state.
          })
          

          【讨论】:

          • 不要那样做。 setState的setter回调应该是pure。此外,在这里,updatedState 将始终是 undefined
          • @EmileBergeron 你有说明回调应该没有副作用的文档的链接吗?
          • 我手头没有链接,但它与严格模式一起记录在案,这有助于识别不需要的副作用。
          【解决方案13】:

          没有任何额外的 NPM 包

          //...
          const BackendPageListing = () => {
              
              const [ myData, setMyData] = useState( {
                  id: 1,
                  content: "abc"
              })
          
              const myFunction = ( x ) => {
                  
                  setPagenateInfo({
                  ...myData,
                  content: x
                  })
          
                  console.log(myData) // not reflecting change immediately
          
                  let myDataNew = {...myData, content: x };
                  
                  console.log(myDataNew) // Reflecting change immediately
          
              }
          
              return (
                  <>
                      <button onClick={()=>{ myFunction("New Content")} }>Update MyData</button>
                  </>
              )
          

          【讨论】:

            【解决方案14】:

            不是说要这样做,但是在没有 useEffect 的情况下不难做到 OP 所要求的。

            使用 promise 来解析 setter 函数主体中的新状态:

            const getState = <T>(
              setState: React.Dispatch<React.SetStateAction<T>>
            ): Promise<T> => {
              return new Promise((resolve) => {
                setState((currentState: T) => {
                  resolve(currentState);
                  return currentState;
                });
              });
            };
            

            这就是您使用它的方式(示例显示了 UI 渲染中 countoutOfSyncCount/syncCount 之间的比较):

            const App: React.FC = () => {
              const [count, setCount] = useState(0);
              const [outOfSyncCount, setOutOfSyncCount] = useState(0);
              const [syncCount, setSyncCount] = useState(0);
            
              const handleOnClick = async () => {
                setCount(count + 1);
            
                // Doesn't work
                setOutOfSyncCount(count);
            
                // Works
                const newCount = await getState(setCount);
                setSyncCount(newCount);
              };
            
              return (
                <>
                  <h2>Count = {count}</h2>
                  <h2>Synced count = {syncCount}</h2>
                  <h2>Out of sync count = {outOfSyncCount}</h2>
                  <button onClick={handleOnClick}>Increment</button>
                </>
              );
            };
            

            【讨论】:

            • setState setter 回调应该是pure
            • 我赞成强调纯函数,但该链接并非特定于 useState 回调(存在不使用纯函数作为回调的用例)。此外,虽然这不是“纯粹的”,但它实际上也不会改变任何状态。我并不是说这是最好的方法(我不使用它),只是它为 OP 问题提供了另一种解决方案
            • 恐怕使用计数和/或回调阻碍了 usestate 本身背后的设计方法。
            • 再一次,不是说这是最好的方法或使用它,只是说它有效。将它与可重现的示例一起使用会比从设计理论层面忽略它更有用
            • 不反对你。我的观点是,没有人给出具体示例或解释此处的警告或缺点,或者显示这与设计不一致的链接。我对反对票没有意见,但我没有发现像“有更好的方法”这样的评论特别有见地,即使它可能是完全有效的,只是展示它或链接到某些东西:)
            【解决方案15】:

            使用 Background Timer 库。它解决了我的问题。

            const timeoutId = BackgroundTimer.setTimeout(() => {
                // This will be executed once after 1 seconds
                // even when the application is the background
                console.log('tac');
            }, 1000);
            

            【讨论】:

            • 添加延迟并不是真正的解决方案。它只是一种解决方法。即使如此,当您可以只使用一个简单的 setTimeout 时,您不需要一个库
            【解决方案16】:
            // replace
            return <p>hello</p>;
            // with
            return <p>{JSON.stringify(movies)}</p>;
            

            现在你应该看到,你的代码实际上工作。 console.log(movies) 不起作用。这是因为movies指向旧状态.如果将 console.log(movies) 移到 useEffect 之外,就在返回的正上方,您将看到更新的电影对象。

            【讨论】:

            • 不知道为什么这个答案被大量否决,它告诉我们如何通过将它放在 useState 函数之外来获得“预期的”console.log 值。简单又甜蜜,如果有人想知道为什么会这样,可以参考上面的详细说明
            • 很好的尝试
            猜你喜欢
            • 1970-01-01
            • 2021-12-12
            相关资源
            最近更新 更多