函數(shù)式組件在不編寫class的情況下使用state以及其他的React特性(比如生命周期)
一、Hooks結(jié)合函數(shù)式組件使用(useState)
通過一個(gè)計(jì)數(shù)器案例,來對(duì)比一下class組件和函數(shù)式組件結(jié)合hooks的對(duì)比:
1、class組件實(shí)現(xiàn):
import React, { PureComponent } from 'react';
class HookTest extends PureComponent {
constructor(props){
super(props)
this.state = {
count: 0
}
this.addCount = this.addCount.bind(this)
}
render() {
return (
<div>
<h2>{this.state.count}</h2>
<button onClick={this.addCount}>增加</button>
</div>
);
}
addCount(){
this.setState({
count: this.state.count+1
})
}
}
export default HookTest;
2、函數(shù)式組件結(jié)合hooks使用:
import React, { useState } from 'react';
function HookTest(){
const [count, setCount] = useState(0) // ES6結(jié)構(gòu)
return (
<div>
<h2>{count}</h2>
<button onClick={()=>setCount(count+2)}>增加</button>
</div>
)
}
export default HookTest
通過上面對(duì)比發(fā)現(xiàn),函數(shù)式組件結(jié)合hooks使用讓代碼看起來更加整齊。
解析:const [count, setCount] = useState(0)
1、其中count是參數(shù),setCount是設(shè)置參數(shù)count的函數(shù);
2、useState是一個(gè)函數(shù),返回一個(gè)數(shù)組,可結(jié)構(gòu)成一個(gè)參數(shù)和設(shè)置參數(shù)的函數(shù),有一個(gè)唯一的參數(shù),設(shè)置的是參數(shù)count的初始值。
3、在一個(gè)函數(shù)組件中定義多個(gè)變量和復(fù)雜變量(數(shù)組、對(duì)象):
import React, { useState } from 'react';
function HookTest(){
const [count, setCount] = useState(0) // ES6結(jié)構(gòu)
const [list, setList] = useState(['A', 'B', 'C'])
const [person, setPerson] = useState({'name': 'Jack'})
return (
<div>
<h2>{count}</h2>
<h2>{person.name}-{person.age}</h2>
<ul>
{
list.map((item, index) => {
return (
<li key={index}>{item}</li>
)
})
}
</ul>
<button onClick={()=>setCount(count+2)}>增加數(shù)字</button>
<button onClick={()=>setList([...list, 'D'])}>修改列表</button>
<button onClick={()=>setPerson({...person, name: 'Lucy', age: 19})}>修改對(duì)象</button>
</div>
)
}
export default HookTest
二、 Effect Hook的使用(useEffect)
1、Effect Hook 類似于class中生命周期的功能
function Index(){
// useEffect頁面初始化使用,相當(dāng)于componentDidMount
useEffect(() => {
console.log('index in');
// return在頁面銷毀時(shí)候調(diào)用,相當(dāng)于componentWillUnmount
return ()=>{
console.log('index out');
}
}, [])
return <h2>index page</h2>
}
2、使用多個(gè)Effect
使用Hook的其中一個(gè)目的就是解決class中生命周期經(jīng)常將很多的邏輯放在一起的問題:
比如網(wǎng)絡(luò)請(qǐng)求、事件監(jiān)聽、手動(dòng)修改DOM,這些往往都會(huì)放在componentDidMount中;
使用Effect Hook,我們可以將它們分離到不同的useEffect中:
import React, { useEffect } from 'react';
export default function MultiUseEffect() {
useEffect(() => {
console.log("網(wǎng)絡(luò)請(qǐng)求");
});
useEffect(() => {
console.log("修改DOM");
})
useEffect(() => {
console.log("事件監(jiān)聽");
return () => {
console.log("取消監(jiān)聽");
}
})
return (
<div>
<h2>MultiUseEffect</h2>
</div>
)
}
Hook 允許我們按照代碼的用途分離它們, 而不是像生命周期函數(shù)那樣:
React 將按照 effect 聲明的順序依次調(diào)用組件中的每一個(gè) effect;
3、 Effect性能優(yōu)化
默認(rèn)情況下,useEffect的回調(diào)函數(shù)會(huì)在每次渲染時(shí)都重新執(zhí)行,但是這會(huì)導(dǎo)致兩個(gè)問題:
某些代碼我們只是希望執(zhí)行一次即可,類似于componentDidMount和componentWillUnmount中完成的事情;(比如網(wǎng)絡(luò)請(qǐng)求、訂閱和取消訂閱);
另外,多次執(zhí)行也會(huì)導(dǎo)致一定的性能問題;
我們?nèi)绾螞Q定useEffect在什么時(shí)候應(yīng)該執(zhí)行和什么時(shí)候不應(yīng)該執(zhí)行呢?
useEffect實(shí)際上有兩個(gè)參數(shù):
參數(shù)一:執(zhí)行的回調(diào)函數(shù);
參數(shù)二:該useEffect在哪些state發(fā)生變化時(shí),才重新執(zhí)行;(受誰的影響)
我們來看下面的一個(gè)案例:
在這個(gè)案例中,我們修改show的值,是不會(huì)讓useEffect重新被執(zhí)行的;
import React, { useState, useEffect } from 'react';
export default function EffectPerformance() {
const [count, setCount] = useState(0);
const [show, setShow] = useState(true);
useEffect(() => {
console.log("修改DOM");
}, [count])
return (
<div>
<h2>當(dāng)前計(jì)數(shù): {count}</h2>
<button onClick={e => setCount(count + 1)}>+1</button>
<button onClick={e => setShow(!show)}>切換</button>
</div>
)
}
但是,如果一個(gè)函數(shù)我們不希望依賴任何的內(nèi)容時(shí),也可以傳入一個(gè)空的數(shù)組 []
三、Context Hook的使用(useContext)
Context Hook可以讓我們通過Hook來直接獲取某個(gè)Context的值:
const value = useContext(MyContext);
在App.js中使用Context:
import React, { createContext } from 'react';
import ContextHook from './04_useContext使用/01_ContextHook';
export const UserContext = createContext();
export const ThemeContext = createContext();
export default function App() {
return (
<div>
<UserContext.Provider value={{name: "why", age: 18}}>
<ThemeContext.Provider value={{color: "red", fontSize: "20px"}}>
<ContextHook/>
</ThemeContext.Provider>
</UserContext.Provider>
</div>
)
}
在對(duì)應(yīng)的函數(shù)式組件中使用Context Hook:
import React, { useContext } from 'react'
import { UserContext, ThemeContext } from '../App'
export default function ContextHook() {
const user = useContext(UserContext);
const theme = useContext(ThemeContext);
console.log(user);
console.log(theme);
return (
<div>
ContextHook
</div>
)
}
四、關(guān)于useReducer Hook的使用 (useReducer)
useReducer僅僅是useState的一種替代方案:
- 在某些場(chǎng)景下,如果state的處理邏輯比較復(fù)雜,我們可以通過useReducer來對(duì)其進(jìn)行拆分;
- 或者這次修改的state需要依賴之前的state時(shí),也可以使用;
單獨(dú)創(chuàng)建一個(gè)reducer/counter.js文件:
export function counterReducer(state, action) {
switch(action.type) {
case "increment":
return {...state, counter: state.counter + 1}
case "decrement":
return {...state, counter: state.counter - 1}
default:
return state;
}
}
home.js文件:
import React, { useReducer } from 'react'
import { counterReducer } from '../reducer/counter'
export default function Home() {
const [state, dispatch] = useReducer(counterReducer, {counter: 100});
return (
<div>
<h2>當(dāng)前計(jì)數(shù): {state.counter}</h2>
<button onClick={e => dispatch({type: "increment"})}>+1</button>
<button onClick={e => dispatch({type: "decrement"})}>-1</button>
</div>
)
}
profile.js文件(創(chuàng)建另外一個(gè)profile.js也使用這個(gè)reducer函數(shù)):
import React, { useReducer } from 'react'
import { counterReducer } from '../reducer/counter'
export default function Profile() {
const [state, dispatch] = useReducer(counterReducer, {counter: 0});
return (
<div>
<h2>當(dāng)前計(jì)數(shù): {state.counter}</h2>
<button onClick={e => dispatch({type: "increment"})}>+1</button>
<button onClick={e => dispatch({type: "decrement"})}>-1</button>
</div>
)
}
通過執(zhí)行可以發(fā)現(xiàn),home.js文件和profile.js文件在使用的時(shí)候,counter并不會(huì)共享,也就是說home文件中修改counter的值不會(huì)影響profile中counter的值,同理修改profile文件中counter也是。
所以,useReducer只是useState的一種替代品,將復(fù)雜的useState拆分到不同的文件中,并不能替代Redux。