我如何保证在使用 useState 添加之前调用 reset

时间:2021-04-13 22:58:39

标签: javascript reactjs

我创建了一个自定义钩子

const useMessageStorage = () => {
  const [messages, setMessages] = useState([]);
  
  const addMessages = (newMessage) => {
    setMessages(oldMessages => [...oldMessages, newMessage)]);
  }

  const clear = () => {
    setMessages([]);
  }

  return { clear, addMessages }
}

在消费者组件内部,我想做一些类似的事情

const { clear, addMessages } = useMessageStorage();
...

clear() // clear before adding new messages
addMessage('new message') 

以上不起作用,因为两个调用都是异步的。但我想在某些特定场景中添加新消息之前清除所有消息。如何解决这个问题?我考虑过使用 useRef,但由于竞争条件我害怕使用它。

1 个答案:

答案 0 :(得分:0)

React 状态更新异步处理的,这是真的,但它们也是按照它们入队的顺序处理的。由于您的addMessages 回调使用功能状态更新,它将从前一个状态更新,不是它被排入队列的渲染周期的状态。

// clear()
setMessages([]); // "draft" state: []

// addMessage(newMessage)
setMessages((oldMessages) => [ // updated from "draft" state: [newMessage]
  ...oldMessages,
  newMessage,
]);

您的代码确实有效。

const useMessageStorage = () => {
  const [messages, setMessages] = React.useState([]);

  const addMessages = (newMessage) => {
    setMessages((oldMessages) => [...oldMessages, newMessage]);
  };

  const clear = () => {
    setMessages([]);
  };

  return { clear, messages, addMessages };
};

function App() {
  const [message, setMessage] = React.useState("");
  const { clear, messages, addMessages } = useMessageStorage();

  const addMessage = () => {
    if (message) {
      addMessages(message);
      setMessage("");
    }
  };

  const clearAndAddMessage = () => {
    if (message) {
      clear();
      addMessages(message);
      setMessage("");
    }
  };

  return (
    <div className="App">
      <label>
        Message:
        <input
          type="text"
          id="message"
          onChange={(e) => setMessage(e.target.value)}
          value={message}
        />
      </label>

      <button type="button" onClick={addMessage}>
        Add Message
      </button>
      <button type="button" onClick={clearAndAddMessage}>
        Clear & Add Message
      </button>

      <ul>
        {messages.map((message, i) => (
          <li key={i}>{message}</li>
        ))}
      </ul>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(
  <App />,
  rootElement
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root" />