useState同步还是异步?

191 阅读2分钟

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

  • 在 react 18 版本以前在同步环境中异步,在异步环境中同步。
  • 在 react 18 版本以后,setState()不论在同步环境还是异步环境都是异步的。

什么是状态撕裂?

  • 如果 count 在更新时,ComponentA 和 ComponentB 渲染的时间不一致,可能会导致 ComponentA 显示旧状态,而 ComponentB 显示新状态,从而发生状态撕裂。
  • 状态更新是异步的,React 的渲染也是异步的,这可能导致不同组件在不同时间渲染出不同的状态。
const CounterContext = createContext();

function App() {
  const [count, setCount] = useState(0);

  return (
    <CounterContext.Provider value={{ count, setCount }}>
      <ComponentA />
      <ComponentB />
    </CounterContext.Provider>
  );
}

function ComponentA() {
  const { count } = useContext(CounterContext);
  return <div>ComponentA: {count}</div>;
}

function ComponentB() {
  const { count } = useContext(CounterContext);
  return <div>ComponentB: {count}</div>;
}

setState

有时异步有时同步

同步环境中异步

  • 在 react 18 版本以前在同步环境中异步,在异步环境中同步。
  • 在 react 18 版本以后,setState()不论在同步环境还是异步环境都是异步的。
export default class Ap extends Component {
  state = {
    a: 0
  };

  tap = () => {
    this.setState(
        { a: this.state.a + 1 }, 
        () => console.log(this.state.a, "能获取到最新值, 相当于 vue 的 nextTick")
    );
    console.log(this.state.a, "无法获取到最新值");
  };

  render() {
    return (
      <div className="App">
        <h1 onClick={this.tap}>{this.state.a}</h1>
      </div>
    );
  }
}

异步环境中同步

  • 在 react 18 版本以前在同步环境中异步,在异步环境中同步。
  • 在 react 18 版本以后,setState()不论在同步环境还是异步环境都是异步的。
export default class Ap extends Component {
  state = {
    a: 0
  };
  /**
   * 在setTimeout 中是同步
   */
  tap = () => {
    setTimeout(() => {
      this.setState({ a: this.state.a + 1 });
      console.log(this.state.a, "--- 能获取到最新值");
    });
  };

  /**
   * 自定义dom事件中是同步
   */
  componentDidMount() {
    document.getElementById("h2")?.addEventListener("click", () => {
      this.setState({ a: this.state.a + 1 });
      console.log(this.state.a, "-- 能获取到最新值");
    });
  }

  render() {
    return (
      <div className="App">
        <h1 onClick={this.tap}>{this.state.a}</h1>
        <h2 id="h2">{this.state.a}</h2>
      </div>
    );
  }
}

同步中异步, 异步环境中同步

  • 在 react 18 版本以前在同步环境中异步,在异步环境中同步。
  • 在 react 18 版本以后,setState()不论在同步环境还是异步环境都是异步的。
// this.v 初始值为0

this.setState({ v: this.v + 1 })
this.setState({ v: this.v + 1 })
console.log(this.v) // 0

setTimeout(() => {
  this.setState({ v: this.v + 1 })
  console.log(this.v) // 2
  this.setState({ v: this.v + 1 })
  console.log(this.v) // 3
})

对象合并

合并的情况


export default class Ap extends Component {
  state = {
    a: 0
  };
  /**
   * 三次setSate相当于一次
   */
  tap = () => {
    this.setState({ a: this.state.a + 1 });
    this.setState({ a: this.state.a + 1 });
    this.setState({ a: this.state.a + 1 });
  };

  render() {
    return (
      <div className="App">
        <h1 onClick={this.tap}>{this.state.a}</h1>
      </div>
    );
  }
}

不合并的写法


export default class Ap extends Component {
  state = {
    a: 0
  };

  tap = () => {
    this.setState((state) => {
      return { a: state.a + 1 };
    });
    this.setState((state) => {
      return { a: state.a + 1 };
    });
    this.setState((state) => {
      return { a: state.a + 1 };
    });
  };

  render() {
    return (
      <div className="App">
        <h1 onClick={this.tap}>{this.state.a}</h1>
      </div>
    );
  }
}