場景一: 更新 state 的一個對象(或數(shù)組)屬性的某個子屬性或值筛欢。
使用 Hook Function Component
function App() {
const [arr, updateArr] = useState([]);
const addList = () => {
arr.push('Hello React');
updateArr(arr);
};
return (
<div>
{
arr.map((item, index) => (
<p key={index}>{index} {item}</p>
))
}
<button onClick={addList}>添加List</button>
</div>
);
}
使用 Class Component
class App extends Component {
constructor(props) {
super(props);
this.state = {
arr: []
}
}
addList = () => {
let arr = this.state.arr;
arr.push('Hello React');
this.setState({
arr: arr
}, () => {
console.log(this.state.arr);
});
};
deleteList = () => {
const { arr } = this.state;
arr.splice(0, 1);
this.setState({
arr: arr
}, () => {
console.log(this.state.arr);
});
};
render() {
const { arr } = this.state;
return (
<div>
{
arr.map((item, index) => (
<p key={index}>{index} {item}</p>
))
}
<button onClick={this.addList}>添加List</button>
<button onClick={this.deleteList}>刪除List</button>
</div>
);
}
}
結(jié)果:使用 Hook Function Component push 數(shù)組后數(shù)組長度并沒有改變,使用Class Component正常榨咐。
原因:在 Hook 中直接修改 state 的一個對象(或數(shù)組)屬性的某個子屬性或值,然后直接進行 set谴供,不會觸發(fā)重新渲染块茁。
- 對 Class Component來說,state 是 Immutable 的桂肌,setState 后一定會生成一個全新的 state 引用数焊。它是通過 this.state 方式讀取 state,所以每次代碼執(zhí)行都會拿到最新的 state 引用崎场。
- 對 Hook Function Component 而言佩耳,useState 產(chǎn)生的數(shù)據(jù)也是 Immutable 的,通過數(shù)組第二個參數(shù) Set 一個新值后谭跨,原來的值會形成一個新的引用在下次渲染時干厚。
解決方案
改變引用地址
function App() {
const [arr, updateArr] = useState([]);
const addList = () => {
arr.push('Hello React');
// 通過擴展運算符實現(xiàn)深拷貝
updateArr([...arr]);
};
return (
<div>
{
arr.map((item, index) => (
<p key={index}>{index} {item}</p>
))
}
<button onClick={addList}>添加List</button>
</div>
);
}
場景二: 在setTimeout中更改state。
使用 Hook Function Component
function App() {
const [count, updateCount] = useState(0);
useEffect(() => {
let timer = setTimeout(() => {
updateCount(1);
getCount();
}, 1000);
return () => {
clearTimeout(timer);
}
}, []);
const getCount = () => {
console.log(count); // result: 0
};
return (
<div>{count}</div>
);
}
使用 Class Component
let timer = null;
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
componentDidMount() {
timer = setTimeout(() => {
this.setState({
count: 1
})
this.getCount();
}, 1000);
}
getCount = () => {
console.log(this.state.count); // result: 1
}
componentWillUnmount() {
clearTimeout(timer);
}
render() {
const { count } = this.state;
console.log(count); // result: 1
return (
<div>{count}</div>
);
}
}
結(jié)果:使用 Hook Function Component 更改count后螃宙,頁面顯示1蛮瞄,getCount方法中打印的count為0,使用Class Component更改count后頁面顯示1谆扎,getCount方法中打印的count為1挂捅。
原因:Hook Function Comoponent中由于對 state 的讀取沒有通過 this. 的方式,使得每次 setTimeout 都讀取了當時渲染閉包環(huán)境的數(shù)據(jù)堂湖,雖然最新的值跟著最新的渲染變了闲先,但舊的渲染里,狀態(tài)依然是舊值无蜂。
解決方案
使用ref
function App() {
const [count, updateCount] = useState(0);
useEffect(() => {
let timer = setTimeout(() => {
updateCount(1);
getCount();
}, 1000);
return () => {
clearTimeout(timer);
}
}, []);
let ref = useRef();
ref.current = count;
const getCount = () => {
console.log(ref.current); // result: 1
};
return (
<div>{count}</div>
);
}
The End~
附上我認為比較值得研讀的相關文章: