在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 。