React更新更新状态变量和组件的输出的教程

1,422 阅读4分钟

React如何更新状态

ReactuseState() 钩子管理功能React组件中的状态。在类组件中this.state 保持状态,你调用特殊方法this.setState() 来更新状态。

大多数情况下,在React中使用状态是直截了当的。然而,在更新状态时,有一个重要的细微差别需要注意。

当你更新组件的状态时,React是立即更新状态(同步)还是安排一个状态更新(异步)?这篇文章回答了这个问题。

1.使用useState() 进行状态更新

考虑一个功能组件DoubleIncreaser

import { useState } from 'react';
function DoubleIncreaser() {
  const [count, setCount] = useState(0);
  const doubleIncreaseHandler = () => {
    setCount(count + 1);
    setCount(count + 1);
  };
  return (
    <>
      <button onClick={doubleIncreaseHandler}>
        Double Increase
      </button>
      <div>Count: {count}</div>
    </>
  );
}

const [count, setCount] = useState(0) 定义了组件的状态。 setCount()是包含当前状态值的状态变量,而 是状态更新器的函数。

该组件有一个按钮Double Increase。当按钮被点击时,doubleIncreaseHandler 事件处理程序对计数状态进行2次连续递增:setCount(count + 1) ,然后再setCount(count + 1)

当点击Double Increase时,该组件的状态是由1 还是2

打开演示,点击双倍增加按钮。每次点击count ,都会增加1

setCount(count + 1) 更新状态时,这些变化不会立即反映在count 变量中。相反,React安排了一次状态更新,在接下来的渲染中,在语句const [count, setCount] = useState(0) ,钩子将新的状态值分配给count

例如:如果count 变量是0 ,那么调用setCount(count + 1); setCount(count + 1); 会被简单地评估为setCount(0 + 1); setCount(0 + 1); - 使得下次渲染时的状态为1

[value, setValue] = useState() 的状态更新函数setValue(newValue) 异步地更新状态。

状态更新函数也接受一个回调,用当前状态计算新的状态。如果是DoubleIncreaser ,可以使用setCount(actualCount => actualCount + 1)

import { useState } from 'react';
function DoubleIncreaser() {
  const [count, setCount] = useState(0);
  const doubleIncreaseHandler = () => {
    setCount(actualCount => actualCount + 1);
    setCount(actualCount => actualCount + 1);
  };
  return (
    <>
      <button onClick={doubleIncreaseHandler}>
        Double Increase
      </button>
      <div>Count: {count}</div>
    </>
  );
}

当使用函数setCount(actualCount => actualCount + 1) 更新状态时,那么actualCount 参数包含状态的实际值。

打开演示,并点击双倍增加按钮。计数的更新由2 ,正如预期的那样。

当然,你可以使用一个中间变量let :

import { useState } from 'react';
function DoubleIncreaser() {
  const [count, setCount] = useState(0);
  const doubleIncrease = () => {
    let actualCount = count;
    actualCount = actualCount + 1;
    actualCount = actualCount + 1;
    setCount(actualCount);
  };
  return (
    <>
      <button onClick={this.doubleIncrease}>
        Double Increase
      </button>
      <div>Count: {count}</div>
    </>
  );
}

let actualCount = count 是一个中间变量,你可以随意更新。更新的中间变量来更新setCount(actualCount)

2.状态变量是不可变的和只读的

如果你忘记了状态变量在下一次渲染时的更新,你可能会尝试在改变状态变量后立即读取它。不幸的是,这并不奏效。

function FetchUsers() {
  const [users, setUsers] = useState([]);
  useEffect(() => {
    const startFetching = async () => {
      const response = await fetch('/users');
      const fetchedUsers = await response.json();
      setUsers(fetchedUsers);
      console.log(users);        // => []
      console.log(fetchedUsers); // => ['John', 'Mike', 'Denis']
    };
    startFetching();
  }, []);
  return (
    <ul>
      {users.map(user => <li>{user}</li>)}
    </ul>
  );
}

FetchUsers 组件在安装时启动一个获取请求-- startFetching()

当获取完成后,setUsers(fetchedUsers) 用获取的用户更新状态。然而,这些变化并没有立即反映在users 状态变量中。

状态变量users 是只读和不可变的。只有useState() 钩子给users 赋值。你不允许手动给状态变量赋值,也不允许改变它。

function FetchUsers() {
  const [users, setUsers] = useState([]);
  useEffect(() => {
    const startFetching = async () => {
      const response = await fetch('/users');
      const fetchedUsers = await response.json();
      users = fetchedUsers;       // Incorrect! users is readonly
      users.push(..fetchedUsers); // Incorrect! users is immutable
      setUsers(fetchedUsers);     // Correct!
    };
    startFetching();
  }, []);
  return (
    <ul>
      {users.map(user => <li>{user}</li>)}
    </ul>
  );
}

3.类组件中的状态更新

异步状态更新的想法对类组件也是有效的。

下面的类组件有一个状态count ,当点击Double Increase按钮的时候,它应该增加2

import { Component } from 'react';
class DoubleIncreaser extends Component {
  state = {
    count: 0
  };
  render() {
    return (
      <>
        <button onClick={this.doubleIncrease}>
          Double Increase
        </button>
        <div>Count: {this.state.count}</div>
      </>
    );
  }
  doubleIncrease = () => {
    // Works!
    this.setState(({ count }) => ({
      count: count + 1
    }));
    this.setState(({ count }) => ({
      count: count + 1
    }));
    // Won't work!
    // this.setState({ count: this.state.count + 1 });
    // this.setState({ count: this.state.count + 1 });
  }
}

看看doubleIncrease() 事件处理程序:状态更新器也使用了一个回调。

打开演示,点击 "双倍增加"按钮。正如预期的那样,this.state.count 更新了2。

在基于类的组件中,this.state 也不会立即更新。当调用this.setState(newState) ,React会安排一次重新渲染,在下次渲染时this.state ,正好包含新的状态值newState

this.setState(newState) 异步地更新 this.state.

4.4.总结

useState() hook和 this.setState()(在类组件内部)异步更新状态变量和组件的输出。

记住这个简单的规则:

调用useState() 钩子的setter函数setValue(newValue) (或类组件的this.setState() )并不完全是更新状态,而是安排一个状态更新