两个计数器代码:
使用 class:
import React, {Component} from "react";
import ReactDOM from "react-dom";
class Example extends Component {
state = {
count: 0
};
componentDidMount() {
setTimeout(() => {
console.log(`You clicked ${this.state.count} times`);
}, 3000);
}
componentDidUpdate() {
setTimeout(() => {
console.log(`You clicked ${this.state.count} times`);
}, 3000);
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({
count: this.state.count + 1
})}>
Click me
</button>
</div>
)
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Example />, rootElement);
使用 hook:
import React, {useState, useEffect} from "react";
import ReactDOM from "react-dom";
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
setTimeout(() => {
console.log(`You clicked ${count} times`);
}, 3000);
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(prevCount => prevCount + 1)}>
Click me
</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Example />, rootElement);
操作: 连续点击 5 次增加按钮:
- hook 的组件 timeout 以后依次显示 1, 2, 3, 4, 5
- class 的组件 timeout 以后依次显示 5, 5, 5, 5, 5
由于函数组件使用闭包的特点, 每一次调用useEffect()
函数, 里面的 count 都是当前的 state, 相当于渲染 5 次, 每次都是一份快照, 里面的 state 也是独立的, 因此最后的结果也是符合直觉
class 组件在每次 state 更新重新渲染以后, 都会去修改 this.props
或者this.state
, 这样永远都是拿到的最新的 state 或者 props
对于函数式组件, 如果想要获取 state 的最新值, 需要使用 ref
同时, 函数式组件里面, 例如使用setCount(count+1)
这种, 这里的count
永远都是一个值, 因此尽量使用setCount(prevCount => prevCount+1)
这种写法进行 state 更新, 如下的例子设置 state 永远只会设置一次, 例如:
setCount(count+1)
setCount(count+1)
由于闭包的原因, 这里相当于:
setCount(2)
setCount(2)
关于useEffect
:
最后, 如果一个函数有使用到 props 或者 state, 那么这个函数会参与到数据流中, 两种办法:
- 使用
useCallback
包裹, 当然其中的 dependency array 里需要包含该函数访问到的组件里的 props 和 state - 直接将该函数放入
useEffect
中, dependency array 里面同样需要包含该函数访问到的 props 和 state