【问题标题】:React hooks with useState使用 useState 反应钩子
【发布时间】:2020-08-06 15:06:52
【问题描述】:

我有以下代码:

export default function App() {
    const [lastMessageId, setLastMessageId] = useState(0);
    const [messages, setMessages] = useState([]);

    const addMessage = (body, type) => {
        const newMessage = {
            id: lastMessageId + 1,
            type: type,
            body: body,
        };
        setLastMessageId(newMessage.id)
        setMessages([...messages, newMessage]);
        console.log("point 1", messages);
        return newMessage.id;
    }

    // remove a message with id
    const removeMessage = (id) => {
        const filter = messages.filter(m => m.id !== id);
        console.log("point 2", filter);
        setMessages(filter);
    }

    // add a new message and then remove it after some seconds
    const addMessageWithTimer = (body, type="is-primary", seconds=5) => {
        const id = addMessage(body, type);
        setTimeout(() => removeMessage(id), seconds*1000);
    };

    return (
        ...
    );
}

我想知道为什么在第 1 点设置消息后,当我执行控制台日志时,它似乎没有更新。当我调用 addMessageWithTimer 时,这会变成一种奇怪的行为,因为当它调用 removeMessage 时,它​​不会正确删除我期望的消息。

你能解释一下怎么做吗?

【问题讨论】:

  • 因为useState 是异步函数。将您的console.log 移到外面

标签: reactjs react-hooks


【解决方案1】:

就像类组件中的setState 一样,useState 的更新函数不会立即更新状态,它们调度要更新的状态。

当您调用 setMessages 时,它会导致 react 安排 App 的新渲染,这将再次执行 App 函数,useState 将返回 messages 的新值。

如果您从纯 JS 的角度考虑它,messages不能改变:它只是一个局部变量(甚至是一个 const)。调用非局部函数不会导致局部变量的值发生变化,JS 就是不能这样工作。

【讨论】:

    【解决方案2】:

    @Retsam 的解释是正确的。

    我认为如果您不在 addMessageWithTimer 中使用 setTimeout,您会遇到问题。不是吗?但就目前而言,它是正确的。

    如果您不想设置 5 秒的计时器,但仍想保持其正常运行,请设置 0 秒的计时器。它仍然可以正常工作。

    【讨论】:

      【解决方案3】:

      你看到了什么奇怪的行为?
      当我尝试您的代码时,我可以在 5 秒后删除添加的消息。

      import React, { useState } from "react";
      import "./styles.css";
      
      export default function App() {
        let bodyText = "";
        const [lastMessageId, setLastMessageId] = useState(0);
        const [messages, setMessages] = useState([]);
      
        const addMessage = (body, type) => {
          if (body === "") return;
          const newMessage = {
            id: lastMessageId + 1,
            type: type,
            body: body
          };
          setLastMessageId(newMessage.id);
          setMessages([...messages, newMessage]);
          bodyText = "";
          return newMessage.id;
        };
      
        // remove a message with id
        const removeMessage = (id) => {
          const filter = messages.filter((m) => m.id !== id);
          console.log("point 2", filter);
          setMessages(filter);
        };
      
        // add a new message and then remove it after some seconds
        const addMessageWithTimer = (body, type = "is-primary", seconds = 5) => {
          const id = addMessage(body, type);
          setTimeout(() => removeMessage(id), seconds * 1000);
        };
        console.log("point 1", messages);
        return (
          <div className="App">
            <h1>Hello CodeSandbox</h1>
            <h2>Start editing to see some magic happen!</h2>
            <input onChange={(e) => (bodyText = e.target.value)} />
            <button onClick={(e) => addMessage(bodyText, "is-primary")}>
              Add messsage
            </button>
            <button onClick={(e) => addMessageWithTimer(bodyText, "is-primary", 5)}>
              Add temp messsage
            </button>
            {messages.map((message, id) => {
              return (
                <div key={id}>
                  <p>
                    {message.id} {message.body}
                  </p>
                </div>
              );
            })}
          </div>
        );
      }
      
      

      【讨论】:

      • 如果我在 5 秒之前添加两条消息,当超时触发删除它时,它会删除所有消息,当第二个触发时它会出现第一个。
      【解决方案4】:

      @Retsam 的回答非常有用,因为我能够理解问题并找到合适的解决方案。

      这是我找到的解决方案:

      export default function App() {
          const [lastMessageId, setLastMessageId] = useState(0);
          const [messages, setMessages] = useState([]);
      
          const addMessage = (body, type="is-primary") => {
              const newMessage = {
                  id: lastMessageId + 1,
                  type: type,
                  body: body
              };
              setLastMessageId(newMessage.id)
              setMessages([...messages, newMessage]);
              return newMessage.id;
          }
      
          // delete messages after 5 seconds
          useEffect(() => {
              if (!messages.length) return;
              const timer = setTimeout(() => {
                  const remainingMessages = [...messages];
                  remainingMessages.shift();
                  setMessages(remainingMessages);
              }, 5*1000);
              return () => clearTimeout(timer);
          }, [messages]);
      
          return (
               ...
          );
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-15
        • 1970-01-01
        • 2021-10-20
        • 2019-08-07
        • 1970-01-01
        • 2021-04-10
        相关资源
        最近更新 更多