Hooks使用條件
目前用create-react-app腳手架創建的項目,react和react-dom版本爲^16.7.0,該版本還未支持hooks的使用,未升級使用會報錯誤:TypeError: Object(...) is not a function。
升級react的版本很簡單,在該項目目錄下執行如下語句即可。
yarn add react@next react-dom@next
完成後可查看package.json文件是否升級成功,該文章代碼是基於react react-dom ^16.8.0-alpha.1版本運行的。
Hooks是寫在React function裏面的,與之前的class不同,就像官網的例子裏一樣。我們可以這樣
const Example = (props) => {
// You can use Hooks here!
return <div />;
}
或者這樣
function Example(props) {
// You can use Hooks here!
return <div />;
}
又或者直接
export default (props) => {
// You can use Hooks here!
return <div />;
}
總之
- 只在 React Functions 調用 Hooks
- 只在頂層調用Hook,不能在循環,條件或嵌套函數中調用Hook
useState
首先需要import一下useState
import React, { useState } from "react";
然後我們就可以在function裏面使用了,以count爲例,方法如下
const [count, setCount] = useState(0);
count是state,0是count的初始值,setCount是設置count的一個函數(函數名不一定要是setXxx,也可以如sCount等,但不建議這麼做),setCount()等效於👇
setCount = (count) => {
this.setState({
count
})
}
useState只能在頂層調用,不能在循環,條件或嵌套函數中調用,因爲useSate是基於數組的實現的,而他們的下標是根據useState出現順序決定的,假設判斷語句中存在useState,當判斷不成立時(即不執行判斷包裹的代碼時),useState會因爲下標錯亂而出錯。想了解更多可看React hooks: not magic, just arrays (需科學上網)或官網的Rules of Hooks
具體使用如下:
import React, { useState } from 'react';
function Cunter() {
const [count, setCount] = useState(0);
return (
<div className="count-box">
<h1 className="title">點了{count}次</h1>
<button onClick={() => {setCount(count + 1);}}>Click Me!</button>
</div>
);
}
export default Cunter;
useEffect
useEffect結合了componentWillMount、componentDidMount和componentWillUnmount的特性。useEffect可以傳入兩個參數,第一個是必選參數,爲function類型,是effect執行的內容,第二爲可選參數,爲數組類型,是判斷是否執行該effect,若第二參數的數值發生變化,則執行。
useEffect(()=>{
console.log('current count:'+count)
})
👆組件加載時、state變化時,都會執行console.log
useEffect(()=>{
console.log('first effect count:'+count)//new count
return function cleanup(){console.log('first effect return count:'+count)}//old count
})
👆組件加載時執行第一個console.log,state變化時,會先執行return的cleanup函數,打印上一次運行時的count,再執行第一個console.log。
注意!這裏上一個運行時的意思是該effect上一次執行時的state,並非是上一個state,這麼說有點繞,看一下這個例子👇
useEffect(()=>{
console.log('third effect count:'+count)
return function cleanup(){console.log('third effect return count:'+count)}
},[count>4?1:0])
👆假設你點擊了8次,當你點第五次時,此時第二參數的內容從0變成了1,所以執行了該effect,打印了
third effect return count:0
third effect count:5
之後再也不執行該effect,直到將這個組件卸載時,纔會執行該effect的cleanup函數,但打印的並不是想象中的 8 ,而是
third effect return count:5
既該effect的上一次運行時的state。
因爲該特性,所以並不推薦第二參數傳遞[]來模擬componentDidMount 和componentWillUnmount,引用官網的原話
While passing [] is closer to the familiar componentDidMount and componentWillUnmount mental model, we suggest not making it a habit because it often leads to bugs, as discussed above. Don’t forget that React defers running useEffect until after the browser has painted, so doing extra work is less of a problem.
完整代碼👇
//Count.jsx
import React, { useState, useEffect } from "react";
function Cunter() {
const [count, setCount] = useState(0);
useEffect(()=>{
console.log('first effect count:'+count)
return function cleanup(){console.log('first effect return cleanup')}
},[])
useEffect(()=>{
console.log('second effect, current count:'+count)//new count
return function cleanup(){console.log('second effect return count:'+count)}//old count
})
useEffect(()=>{
console.log('third effect count:'+count)
return function cleanup(){console.log('third effect return count:'+count)}
},[count>4?1:0])
return (
<div className="count-box">
<h1 className="title">點了{count}次</h1>
<button onClick={() => {setCount(count + 1);}}>Click Me!</button>
</div>
);
}
export default Cunter;
//App.jsx
import React, { Component } from "react";
import Cunter from "./Cunter";
class App extends Component {
constructor(props) {
super(props);
this.state = { show: true };
}
render() {
return (
<div>
{this.state.show ? <Cunter /> : <p>cleanup</p>}
<br/>
<button onClick={() => {this.setState({ show: false })}}>cleanup</button>
</div>
);
}
}
export default App;