-1

我是新手React,我创建了一个最小的测试应用程序来说明这个scope问题。

import React, { useState, useEffect } from 'react';
import logo from './logo.svg';
import './App.css';


function useCounter(topic, defaultMsg) {
  const [message, setMessage] = useState(defaultMsg);

  const increment=() =>{
      console.log("increment from " + message);
      setMessage(message+1);
  }

  return [message, increment];
}

export default function App() {
  const [counter, incrementCounter] = useCounter("anyData", 0);


  return (
    <div>
      <p>you clicked: {counter} times</p>

      <button
        onClick={() => {
          console.log("sending ");
          incrementCounter(counter); //works 
          incrementCounter(counter); //doesn't work because using the old scope
        }}
      >
        Click me
      </button>
    </div>
  );
}

输出

App.js:10 increment from 0
App.js:10 increment from 0  //should be 1
App.js:27 sending 
App.js:10 increment from 1 
App.js:10 increment from 1  //should be 2

如果按下按钮,则incrementCounter调用两次。预期的行为:每次单击都会将计数器增加 2。但是,这并不是因为useCounter钩子似乎使用了 message 的旧值。所以每次点击都会使计数器增加 1。我知道为什么这不起作用,我不知道如何解决这个问题。

你会如何解决这个问题?

4

1 回答 1

1

问题是事件处理程序中的状态更新调用是批处理的,因此在不使用回调方法的情况下多次调用 setState 将无法正确更新值

想想代码

      incrementCounter(counter); 
      incrementCounter(counter); 

作为

 setMessage(message + 1);
 setMessage(message + 1);

现在这两个调用都合并了,将应用于 setState 的最终更改是message + 1which 1

状态更新也是异步的,不会立即反映

如果您将 setMessage 实现更改为function state update

setMessage(prevMessage => prevMessage+1);

您将看到您期望的输出,因为在这种情况下,react 不执行批处理,并且状态将增加两次。但是,由于 setState 的异步性质,您仍然会在控制台中看到相同的输出

示例演示

于 2020-05-07T17:52:32.770 回答