useState和setState

96 阅读2分钟

在react文档中看到一个state状态更新的例子:zh-hans.react.dev/learn/state… ,觉得很疑惑。

我使用 preact(和react差别基本差不多,方便看源码) 写了个demo:

import { render } from "preact";
import { useState } from "preact/hooks";

export function App() {
  const [count, setCount] = useState(1);
  const increment = () => {
    setCount(2);
    /** 1 */
    console.log('count',count)
    setTimeout(()=>{
      /** 1 */
      console.log("count",count);
    },1000)
  }
  return <div>{count}
  <div>
    <button onClick={increment}>add</button>
  </div>
  </div>;
}

render(<App />, document.getElementById("app"));

调用increment,setCount(2),因为是异步的,此时打印count输出1,这个没问题。但是等1s后,count还是1?虽然根据react的快照理论,也能解释得通,而且count也是常量。但是给我得感觉就是怪怪的。

我把这段程序等价的改写成 class component 的形式:

import { render,Component } from "preact";
interface State {
  count: number;
}
export class App extends Component<{},State> {
  constructor(props) {
    super(props);
    // 初始化 state
    this.state = {
      count: 1
    };
    // 绑定事件处理函数
    this.increment = this.increment.bind(this);
  }

  increment() {
    // 更新 state
    this.setState({count:2});
    /** 1 */
    console.log("count", this.state.count);
    /** 2 */
    setTimeout(() => {
      console.log("count", this.state.count);
    }, 0);
  }

  render() {
    return (
      <div>
        {this.state.count}
        <div>
          <button onClick={this.increment}>add</button>
        </div>
      </div>
    );
  }
}
render(<App />, document.getElementById("app"));

现在timeount后,打印的却是2? 那严格来说这两个代码并不等价。 我更喜欢 class component 这种形式,比较符合人的直觉。

在solidjs中,却不是这样的:

import { render } from "solid-js/web";
import { createSignal } from "solid-js";

import "./style.css";

function App() {
  const [count, setCount] = createSignal(1);

  function handleMouseMove(event) {
    setCount(2);
    // 2
    console.log('count',count())
    setTimeout(()=>{
      // 2
      console.log('cc',count())
    },1000)
	}

  return (
    <div onClick={handleMouseMove}>
      The mouse position {count}
    </div>
  );
}

render(() => <App />, document.getElementById('app'));

可以看到,setCount(2)之后,打印count的值是实时生效的。 不一样的地方在于:solidjs不是基于虚拟dom的,没有vnode的存在,当状态更新时,App函数不会重新调用

solidjs和react虽然都是函数式组件,但是区别在于:solidjs的函数是执行一次的,后续的状态更新不会重新执行函数。react每次重新渲染都是通过重新执行函数完成的,讲究一个pure!

useState解释

react实际上把状态的记忆和管理放在了setState内部。函数式组件本质上可以看作是组件实例的渲染函数render。当第一次创建组件实例并调用渲染函数时,const [count, setCount] = useState(1) 这行代码初始化一个状态为1,并且返回返回当前的状态值,复给常量count。那么在这个渲染函数中对count的所有访问得到的值都是1。但组件后续重新渲染时,会继续执行这行代码 const [count, setCount] = useState(1),此时useState(1)返回的就不是1了,因为useState是关联到了组件实例,组件实例是没有销毁重建的,只是重新调用了render函数。count的状态记录在了组件实例内部。所以此时 useState(1) 返回的其实是最新的状态 2 。