React元件分為類式元件與函數式元件,也成為狀态元件與無狀态元件,當你的元件需要狀态對象時,大多數的寫法都是Class,它有this、有生命周期、而function既沒有自己的this,也沒有生命周期。如何使用function來編寫有狀态、有生命周期的元件呢,那麼就要介紹React
16.8版本後退出的新特性——Hooks
React Hooks
- useState(狀态)
- useRef(Ref)
- useEffect(副作用)
- useContext(上下文)
- useReducer(Redux)
- 自定義Hook
useState
useState鈎子可以建立一個狀态對象以及設定狀态對象的方法,用法如下:
其中
useState(initState, setState)
接收兩個參數,第一個參數
initState
為初始化狀态資料,第二個參數
setState
為設定狀态的方法,一般第一個參數需要設定,如果不設定count則為undefined。
例子:
import { useState } from "react";
import "./styles.css";
export default function App() {
const [count, setCount] = useState(0);
const add = () => {
setCount(count + 1);
};
return (
<div className="App">
<h1>我是父元件</h1>
<p>Count: {count}</p>
<button onClick={add}>加1</button>
</div>
);
}
這裡由useState建立出的count類似于Class元件中state中建立的對象,而setCount與Class元件中的setState相似(都可以傳遞一個值或者一個函數)
useRef
useRef用于建立一個ref對象,可以在必要的時候在ref對象中
current
中讀取該ref的值,用法如下:
同樣
useRef(initRef)
也可以傳遞一個參數,相當于給ref的
current
建立一個預設值
例子:
import { useState, useRef } from "react";
import "./styles.css";
export default function App() {
const [count, setCount] = useState(0);
const InputRef = useRef();
const add = () => {
setCount(count + InputRef.current.value * 1);
};
return (
<div className="App">
<h1>我是父元件</h1>
<p>Count: {count}</p>
<input ref={InputRef} />
<button onClick={add}>求和</button>
</div>
);
}
useEffect
開篇說了function元件沒有自己的狀态與生命周期,通過
useState
可以建立狀态對象,而使用
useEffect
可以模拟生命周期,例如Class元件中的
componentDidMount
、
componentDidUpdate
和
componentWillUnMount
生命周期。
useEffect接受兩個參數,可以通過傳遞不同的參數來模拟上述的生命周期
模拟
componentDidMount
例子:
import { useState, useEffect } from "react";
import "./styles.css";
export default function App() {
const [count, setCount] = useState(0);
const add = () => {
setCount(count + 1);
};
useEffect(() => {
console.log("useEffect,count:",count);
},[]);
return (
<div className="App">
{console.log("render, count:",count)}
<h1>我是父元件</h1>
<p>Count: {count}</p>
<button onClick={add}>求和</button>
</div>
);
}
當元件第一次渲染後,可以在控制台看到,
useEffect
在元件挂載後執行,此時和Class元件中的
componentDidMount
作用是一樣的。
當你再次點選後,你會發現,隻有元件被更新了,而
useEffect
不輸出了
模拟
componentDidUpdate
例子:
import { useState, useEffect } from "react";
import "./styles.css";
export default function App() {
const [count, setCount] = useState(0);
const [Name, setName] = useState("法外狂徒");
const add = () => {
setCount(count + 1);
};
const changeName = () => {
setName((preName) => preName === "法外狂徒" ? "張三" : "法外狂徒")
}
useEffect(() => {
console.log(`useEffect,count:${count}, Name:${Name}`);
}, [count]);
return (
<div className="App">
{console.log(`render,count:${count}, Name:${Name}`)}
<h1>我是父元件</h1>
<p>Name: {Name}</p>
<p>Count: {count}</p>
<button onClick={add}>求和</button>
<button onClick={changeName}>換名</button>
</div>
);
}
此時,點元件挂載後,分别點選求和按鈕與換名按鈕後會發現,點選求和後useEffect會輸出,狀态也發生了改變,而點選換名按鈕後,雖然狀态發生了改變,但是useEffect卻沒有輸出。因為在useEffect中傳遞的第二個參數中,隻對
count
進行了更新的監聽(有點Vue中的Watch内味了嗎),當
count
狀态更新後,會執行傳遞的方法,而其他狀态更新就不會執行。
模拟
componentWillUnMount
例子:
import { useState, useEffect } from "react";
import "./styles.css";
export default function App() {
const [flag, setFlag] = useState(true);
const switchComponent = () => {
setFlag(!flag);
};
useEffect(() => {
console.log(`useEffect App`);
return () => {
console.log("Component unMount");
};
}, []);
return (
<div className="App">
{console.log(`render`)}
<h1>我是父元件</h1>
<button onClick={switchComponent}>切換</button>
{flag ? <A /> : <B />}
</div>
);
}
function A() {
useEffect(() => {
console.log(`useEffect >>> A`);
return () => {
console.log("A Component unMount");
};
}, []);
return <div>我是A元件</div>;
}
function B() {
useEffect(() => {
console.log(`useEffect >>> B`);
return () => {
console.log("B Component unMount");
};
}, []);
return <div>我是B元件</div>;
}
上述例子中,模拟了一個元件加載與解除安裝的效果,點選切換按鈕可以将A元件與B元件來回挂載到頁面中。
當第一次頁面渲染時,父元件和子元件的
useEffect
都會被執行,和Classs元件的生命周期相同。
第一次點選切換按鈕,A元件解除安裝,B元件挂載
這時候可以發現,A元件解除安裝的時候,執行了
useEffect
傳回的方法,而這個方法正是模拟了Class元件中的
componentWillUnMount
生命周期。
第二次點選切換按鈕,B元件解除安裝,A元件挂載
可以通過這個方法來在function元件模拟
componentWillUnMount
useContext
在Class元件中,元件執行個體對象中有一個
context
屬性用于隔層傳遞資料,在function元件中,可以使用
useContext
來進行建立
context
例子:
import { useState, useContext, createContext } from "react";
import "./styles.css";
const AppContext = createContext({});
export default function App() {
const [ name, setName ] = useState("張三");
return (
<div>
<h1>我是父元件</h1>
<AppContext.Provider value={{ name: name }}>
<Child />
</AppContext.Provider>
</div>
);
}
function Child() {
return (
<div>
<h2>我是子元件</h2>
<Grand />
</div>
);
}
function Grand() {
const { name } = useContext(AppContext);
return (
<div>
<h3>我是孫元件: App元件name為:{name}</h3>
</div>
);
}
在父元件中通過
createContext
來建立出一個上下文,并在挂載子元件的位置提供
Provider
來給後代元件中提供資料,在需要使用資料的子孫元件中,使用
useContext
來使用父元件傳遞過來的資料。
useReducer
顧名思義,
useReducer
可以讓function元件使用redux,用法:
useReducer
接收三個參數,第一個參數為改變狀态的reducer方法,第二個參數為初始化的state資料,第三個參數可以接收一個惰性初始化函數,用于計算傳回一個初始化state資料。
例子:
import { useReducer, useRef } from "react";
import "./styles.css";
const initState = 0;
const reducer = (state = initState, action) => {
switch (action.type) {
case "add":
return state + action.value;
case "minus":
return state - action.value;
default:
return state;
}
};
export default function App() {
const InputRef = useRef();
const [state, dispatch] = useReducer(reducer, initState);
const add = () => {
dispatch({ type: "add", value: InputRef.current.value * 1 });
};
const minus = () => {
dispatch({ type: "minus", value: InputRef.current.value * 1 });
};
return (
<div className="App">
<h1>我是父元件</h1>
<p>Count: {state}</p>
<input ref={InputRef} />
<button onClick={add}>加</button>
<button onClick={minus}>減</button>
</div>
);
}
自定義Hook
我們還可以通過自定義一些Hook來滿足我們開發的需要,例如,封裝一個擷取清單資料的Hook,
僞代碼:
import { useState, useEffect } from "react";
import "./styles.css";
const fetchInitData = () => {
const [dataList, setDataList] = useState([])
useEffect(async () => {
await ajax.get('/getDataList').then(res => {
setDataList(dataList)
})
},[])
return {
dataList,
setDataList
}
}
export default function App() {
useEffect(() => {
fetchInitData()
},[])
return (
<div className="App">
<h1>我是父元件</h1>
</div>
);
}
碼字不易,給個贊再走吧~