如何有意减慢 React 状态更新 - 批量更新

发布于 2025-01-14 12:32:01 字数 347 浏览 1 评论 0原文

有时我可能想卸载并重新安装其中包含新数据的组件。这可能看起来像:

setAllPosts(undefined);
setAllPosts(newArrayOfPosts);

因为 React 会批量更改状态,所以状态不会更改,具体取决于 newArrayOfPosts 的来源。我已经能够用 1 秒的 setTimeout() 破解解决方案,然后填写 setAllPosts(),但这感觉很错误。

有没有最佳实践方法来告诉 React 放慢速度一会儿?或者也许不批量更新这个特定的状态更改?

PS 我知道有更好的方法可以做到这一点,但我在第三方环境中工作,并且我所能访问的内容非常有限。

Occasionally I may want to unmount and remount a component with new data inside it. This could look like:

setAllPosts(undefined);
setAllPosts(newArrayOfPosts);

Because React batches state changes, depending on where the newArrayOfPosts is coming from, the state won't change. I've been able to hack a solution with a setTimeout() of 1 second and then filling in setAllPosts(), but this feels so wrong.

Is there a best practice way to tell React to slow down for a moment? or maybe to not batch update this particular state change?

P.S. I know there are better ways to do this, but I am working inside a third party environment and am pretty limited to what I have access to.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

我ぃ本無心為│何有愛 2025-01-21 12:32:02

这就是你可以做到的 -

import { flushSync } from 'react-dom';

const handleClick = () => {
  flushSync(() => {
    setAllPosts(undefined);
  // react will create a re-render here
  });
  
  flushSync(() => {
    setAllPosts(newArrayOfPosts);
  // react will create a re-render here
  });
};

这用于取消批处理反应状态。这只是一种单一的方法。另一种方法是使用 setTimeout。请注意,在 React 18 版本中,setTimeouts 内的状态更新也会进行批处理 - 这称为自动批处理,但我们仍然可以通过使用不同的 setTimeouts 来实现这一点 -

const handleClick = () => {
  setTimeout(() => {
    setAllPosts(undefined);
  // react will create a re-render here
  }, 10);
  
  setTimeout(() => {
    setAllPosts(newArrayOfPosts);
  // react will create a re-render here
  },20);
};

只要确保保持时间差以排除批处理完成即可通过反应。

This is how you can do it -

import { flushSync } from 'react-dom';

const handleClick = () => {
  flushSync(() => {
    setAllPosts(undefined);
  // react will create a re-render here
  });
  
  flushSync(() => {
    setAllPosts(newArrayOfPosts);
  // react will create a re-render here
  });
};

This is used to un-batch the react states. This is just a single way of doing it. The other way could be to use setTimeout. Please note that with version 18 of react, state updates within setTimeouts are also being batched - this is known as Automatic Batching, but we still can achieve this by using different setTimeouts -

const handleClick = () => {
  setTimeout(() => {
    setAllPosts(undefined);
  // react will create a re-render here
  }, 10);
  
  setTimeout(() => {
    setAllPosts(newArrayOfPosts);
  // react will create a re-render here
  },20);
};

Just make sure to keep a time difference to rule out the batching done by React.

莫多说 2025-01-21 12:32:02

一旦 React 18 可用(目前是候选版本),将会有一个函数可以强制更新不进行批处理: flushSync

import { flushSync } from 'react-dom';

flushSync(() => {
  setAllPosts(undefined);
});
flushSync(() => {
  setAllPosts(newArrayOfPosts);
});

在那之前,您可能需要执行 setTimeout 方法(尽管它不会不需要是一整秒)。

PS 我知道有更好的方法可以做到这一点,但我在第三方环境中工作,并且我可以访问的内容非常有限。

是的,如果你能做点别的事情可能会更好。大多数时候,如果您想故意卸载/重新安装组件,最好通过使用key来实现,您可以在希望重新安装时更改该键。

const [key, setKey] = useState(0);
const [allPosts, setAllPosts] = useState([]);

// ...
setKey(prev => prev + 1);
setAllPosts(newArrayOfPosts);

// ...
return (
   <SomeComponent key={key} posts={allPosts} />
)

Once react 18 is available (it's currently a release-candidate) there will be a function that can force updates to not be batched: flushSync

import { flushSync } from 'react-dom';

flushSync(() => {
  setAllPosts(undefined);
});
flushSync(() => {
  setAllPosts(newArrayOfPosts);
});

Until then, you may need to do the setTimeout approach (though it doesn't need to be a whole second).

P.S. I know there are better ways to do this, but I am working inside a third party environment and am pretty limited to what I have access to.

Yeah, if you can do something else that would probably be better. Most of the time, if you want to deliberately unmount/remount a component, that is best achieved by using a key which you change when you want the remount to happen.

const [key, setKey] = useState(0);
const [allPosts, setAllPosts] = useState([]);

// ...
setKey(prev => prev + 1);
setAllPosts(newArrayOfPosts);

// ...
return (
   <SomeComponent key={key} posts={allPosts} />
)
流星番茄 2025-01-21 12:32:02

有时我可能想卸载并重新安装其中包含新数据的组件。

听起来这个用例需要一个 useEffect() ,它具有基于您关心的事物的依赖关系,例如提供给该组件的另一条状态或道具。

useEffect(() => {
   setAllPosts(newArrayOfPosts);
}, [shouldUpdate]);

我什至看到过人们通过名为 countrenderCount 的状态依赖项来触发 useEffect() 的示例。不确定这是否一定是最佳实践,但这是解决问题的一种方法。

const [count, setCount] = useState(0);
const [allPosts, setAllPosts] = useState([]);

useEffect(() => {
   setAllPosts(props.values);
}, [count]);

const handleChange = () => {
   setCount(prevCount => prevCount + 1); // This will trigger your useEffect when handleChange() in invoked
}

Occasionally I may want to unmount and remount a component with new data inside it.

It sounds like this use-case calls for a useEffect() with a dependency based on something you care about, like another piece of state or prop being provided to this component.

useEffect(() => {
   setAllPosts(newArrayOfPosts);
}, [shouldUpdate]);

I've even seen examples of people triggered useEffect() with a dependency of a piece of state called count or renderCount. Not sure if this is necessarily best practice but it's one way to go about things.

const [count, setCount] = useState(0);
const [allPosts, setAllPosts] = useState([]);

useEffect(() => {
   setAllPosts(props.values);
}, [count]);

const handleChange = () => {
   setCount(prevCount => prevCount + 1); // This will trigger your useEffect when handleChange() in invoked
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文