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

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

State not update inside setInterval in React useEffect

问题

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

const sampleText = 'Hello my customer, how can I help you?';

const [conversation, setConversation] = useState([]);

useEffect(() => {
    let i = 0;

    const textToArray = sampleText.split(' ');

    const newText = [...conversation];

    // 创建第一次的初始化状态
    if (newText.length < 1) {
        newText.push({
            id: Math.random(),
            reply_as: 'bot',
            message: '',
        });
    }

    // 开始生成机器人消息以回复
    const timer = setInterval(() => {
        if (i < textToArray.length) {
            // 将最新项的消息属性与 newText 数组中的每个字符串连接起来
            newText[newText.length - 1].message += textToArray[i];
            setConversation(newText);
        } else {
            clearInterval(timer);
        }

        i++;
    }, 300);

    return () => clearInterval(timer);
}, []);

return (
    <div className="message-list">
        {conversation.map(item => (             
            <div>
                <span>Sender: {item.reply_as}</span>
                <p>{item.message}</p>
            </div>
        ))}
    </div>
)

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

英文:

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?

const sampleText = &#39;Hello my customer, how can I help you?&#39;;

const [conversation, setConversation] = useState([]);

useEffect(() =&gt; {
    let i = 0;

    const textToArray = sampleText.split(&#39; &#39;); 

    const newText = [...conversation];

    // create initialization state for the first time
    if (newText.length &lt; 1) {
        newText.push({
            id: Math.random(),
            reply_as: &#39;bot&#39;,
            message: &#39;&#39;,
        });
    }

    // begining to generate bot message to reply
    const timer = setInterval(() =&gt; {
        if (i &lt; textToArray.length) {
            // concate a message property in latest item with each string in newText array
            newText[newText.length - 1].message += textToArray[i];
            setConversation(newText);
        } else {
            clearInterval(timer);
        }

        i++;
    }, 300);

    return () =&gt; clearInterval(timer);
}, []);

return (
   &lt;div className=&quot;message-list&quot;&gt;
      {conversation.map(item =&gt; (             
         &lt;div&gt;
            &lt;span&gt;Sender: {item.reply_as}&lt;/span&gt;
            &lt;p&gt;{item.message}&lt;/p&gt;
         &lt;/div&gt;
      ))}
   &lt;/div&gt;
)

答案1

得分: 4

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

setConversation([...newText]);


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

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

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:

确定