最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - State is not updated in StrictMode - Stack Overflow

programmeradmin2浏览0评论

After some trial I discovered following problem occurs in strict mode. I would be interested if someone can explain why.

Take this simple example where inside render I am just scheduling a timeout which updates state:

Example:

let firstRender = true; // Normally I would use ref but I was playing with example

export default function App() {
  let [data, setData] = React.useState({ name: 'Nick' });

  // Schedule a timeout on first render
  if (firstRender) {
    setTimeout(() => {
      console.log('Running');

      setData((ps) => ({
        ...ps,
        name: 'Paul',
      }));
    }, 1000);
  }

  console.log('Running render');
  firstRender = false;

  return (
    <div>
      <h1>{data.name}</h1>
      <p>Start editing to see some magic happen :)</p>
    </div>
  );
}

If you run this example without Strict mode, then you will see "Paul" on screen after one second, as I was expecting.

If you use Strict mode, it will always show "Nick" on screen. Idea why?

Note: This behavior can't be repeated if you use useRef instead of the global variable firstRender in Strict mode. This is due to the fact that in strict mode, during mount useRef will be initialized twice. So in our case the value assigned during first render to the ref will be lost.

After some trial I discovered following problem occurs in strict mode. I would be interested if someone can explain why.

Take this simple example where inside render I am just scheduling a timeout which updates state:

Example:

let firstRender = true; // Normally I would use ref but I was playing with example

export default function App() {
  let [data, setData] = React.useState({ name: 'Nick' });

  // Schedule a timeout on first render
  if (firstRender) {
    setTimeout(() => {
      console.log('Running');

      setData((ps) => ({
        ...ps,
        name: 'Paul',
      }));
    }, 1000);
  }

  console.log('Running render');
  firstRender = false;

  return (
    <div>
      <h1>{data.name}</h1>
      <p>Start editing to see some magic happen :)</p>
    </div>
  );
}

If you run this example without Strict mode, then you will see "Paul" on screen after one second, as I was expecting.

If you use Strict mode, it will always show "Nick" on screen. Idea why?

Note: This behavior can't be repeated if you use useRef instead of the global variable firstRender in Strict mode. This is due to the fact that in strict mode, during mount useRef will be initialized twice. So in our case the value assigned during first render to the ref will be lost.

Share Improve this question edited Feb 17 at 13:10 gmoniava asked Sep 30, 2021 at 13:42 gmoniavagmoniava 28.8k9 gold badges57 silver badges99 bronze badges 4
  • Just to be clear, the word "text", is turning to the word "Nick" after 1 sec? – Rao Virinchi Commented Sep 30, 2021 at 13:53
  • @RaoVirinchi I simplified example. Yeah note the differences in rendered result between using strict mode and not using it – gmoniava Commented Sep 30, 2021 at 13:57
  • Don't use normal variable use a ref – Shivam Jha Commented Sep 30, 2021 at 14:02
  • @ShivamJha Even if that fixes I would be interested why such behavior happens – gmoniava Commented Sep 30, 2021 at 14:03
Add a ment  | 

2 Answers 2

Reset to default 9

This is due to the fact that strict mode intentionally invokes your function ponent body twice (when in dev mode) to help spot unintended side effects.

On the second invocation, your firstRender variable is false so your setTimeout doesn't run.

Important to note that this second invocation isn't just a re-render like you'd get from a state update. It's a second invocation of the entire ponent body. State is not preserved. React invokes your ponent function once, discards the result, and invokes it a second time to get the output.

From the docs:

Because the above methods might be called more than once, it’s important that they do not contain side-effects.

Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:

  • Function ponent bodies

Q. it seems using useRef instead of the global variable firstRender fixes this problem also in Strict mode. Curious why that's the case?

According to React Docs,

Ref is also useful to keep some mutable value around. The value stores in ref, will not change during subsequent re-renders, unless explicitly changed.

Therefore, for values you don't want to change (maybe some expensive calculation) or for other reasons, use useRef.

The “ref” object is a generic container whose current property is mutable and can hold any value, similar to an instance property on a class.

This works because useRef() creates a plain JavaScript object. The only difference between useRef() and creating a {current: ...} object yourself is that useRef will give you the same ref object on every render.

Keep in mind that useRef doesn’t notify you when its content changes. Mutating the .current property doesn’t cause a re-render. If you want to run some code when React attaches or detaches a ref to a DOM node, you may want to use a callback ref instead.

发布评论

评论列表(0)

  1. 暂无评论