在React的`useEffect`中,`setInterval`内部状态没有更新。

huangapple go评论94阅读模式
英文:

State not update inside setInterval in React useEffect

问题

我正在创建一个机器人,在组件挂载时自动生成文本,但是在useEffect中使用setInterval时出现了问题。根据我的期望,我想要将字符串中的每个单词连接在一个示例文本中,并将其更新到状态,直到所有操作完成,但我的代码不起作用,屏幕上只显示第一个单词Hello。我做错了什么,如何解决这个问题?

  1. const sampleText = 'Hello my customer, how can I help you?';
  2. const [conversation, setConversation] = useState([]);
  3. useEffect(() => {
  4. let i = 0;
  5. const textToArray = sampleText.split(' ');
  6. const newText = [...conversation];
  7. // 创建第一次的初始化状态
  8. if (newText.length < 1) {
  9. newText.push({
  10. id: Math.random(),
  11. reply_as: 'bot',
  12. message: '',
  13. });
  14. }
  15. // 开始生成机器人消息以回复
  16. const timer = setInterval(() => {
  17. if (i < textToArray.length) {
  18. // 将最新项的消息属性与 newText 数组中的每个字符串连接起来
  19. newText[newText.length - 1].message += textToArray[i];
  20. setConversation(newText);
  21. } else {
  22. clearInterval(timer);
  23. }
  24. i++;
  25. }, 300);
  26. return () => clearInterval(timer);
  27. }, []);
  28. return (
  29. <div className="message-list">
  30. {conversation.map(item => (
  31. <div>
  32. <span>Sender: {item.reply_as}</span>
  33. <p>{item.message}</p>
  34. </div>
  35. ))}
  36. </div>
  37. )

以上是您提供的代码的翻译部分。

英文:

I am creating a bot to auto generate text when component is mounted, but I have a problem when setInterval in useEffect. As my expected, I want to concate each word from the string in an sample text and update it into state until everything is done but my code not works, I only get a first word Hello in screen. What I am wrong and how I can resolve this problem?

  1. const sampleText = &#39;Hello my customer, how can I help you?&#39;;
  2. const [conversation, setConversation] = useState([]);
  3. useEffect(() =&gt; {
  4. let i = 0;
  5. const textToArray = sampleText.split(&#39; &#39;);
  6. const newText = [...conversation];
  7. // create initialization state for the first time
  8. if (newText.length &lt; 1) {
  9. newText.push({
  10. id: Math.random(),
  11. reply_as: &#39;bot&#39;,
  12. message: &#39;&#39;,
  13. });
  14. }
  15. // begining to generate bot message to reply
  16. const timer = setInterval(() =&gt; {
  17. if (i &lt; textToArray.length) {
  18. // concate a message property in latest item with each string in newText array
  19. newText[newText.length - 1].message += textToArray[i];
  20. setConversation(newText);
  21. } else {
  22. clearInterval(timer);
  23. }
  24. i++;
  25. }, 300);
  26. return () =&gt; clearInterval(timer);
  27. }, []);
  28. return (
  29. &lt;div className=&quot;message-list&quot;&gt;
  30. {conversation.map(item =&gt; (
  31. &lt;div&gt;
  32. &lt;span&gt;Sender: {item.reply_as}&lt;/span&gt;
  33. &lt;p&gt;{item.message}&lt;/p&gt;
  34. &lt;/div&gt;
  35. ))}
  36. &lt;/div&gt;
  37. )

答案1

得分: 4

  1. 你需要提供一个新的数组引用,否则 React 不会重新渲染组件。

setConversation([...newText]);

  1. 第一个 `setConversation` 触发了重新渲染,这就是为什么显示了 "Hello",但后续对 `setConversation` 的调用没有触发重新渲染,因为数组引用保持不变。
英文:

You need to provide a new array reference, otherwise React doesn't re-render the component.

  1. setConversation([...newText]);

The first setConversation triggered a re-render, that's why "Hello" was shown, but the following calls to setConversation didn't trigger a re-render because the array reference stayed the same.

huangapple
  • 本文由 发表于 2023年2月24日 03:02:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/75549233.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定