到底React Hooks有何特別(二)?淺談useEffect及useReducer
於本篇文章的上集,我們討論了useState
如何令Stateful React component
簡化良多,此篇主要討論的是如何使
用useEffect
。useEffect
可以簡化stateful logic,很多人都提到 React Hooks
有可能可以完全取代Redux
作為 React State
Management的標準,正因如此。
重溫
上集提到,使用useState
可以將原本class based component
變成簡單的 functional component
。
class Welcome extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 0,
};
this.incrementCounter = this.incrementCounter.bind(this);
}
incrementCounter() {
this.setState((state) => ({
counter: state.counter + 1,
}));
}
render() {
return (
<h1 onClick={this.incrementCounter}>
Hello, {this.props.name}
{this.state.counter} times
</h1>
);
}
}
變成
function Welcome(props) {
const [counter, setCounter] = useState(0);
return (
<h1 onClick={() => setCounter((counter) => counter + 1)}>
Hello, {props.name}
{counter} times
</h1>
);
}
UseEffect
此例子中並無諸如componentDidUpdate
、componentDidMount
、componentWillUnmount
等方法,React Hooks
到底如何有效於
function 中取代呢?答案就是運用 useEffect
。
舉例加上componentDidMount
及componentWillUnmount
,來初始化及重置counter。
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 0,
};
this.incrementCounter = this.incrementCounter.bind(this);
}
componentDidMount() {
this.setState({
counter: 10,
});
}
componentWillUnmount() {
this.setState({
counter: 0,
});
}
incrementCounter() {
this.setState((state) => ({
counter: state.counter + 1,
}));
}
render() {
return (
<h1 onClick={this.incrementCounter}>
Hello, {this.props.name}
{this.state.counter} times
</h1>
);
}
}
componentDidMount
算是component的setup logic,當component一載入完成就會開始運行。
componentWillUnmount
算是component的teardown logic,當component臨缷載前開始運行。
以useEffect
方法寫,出乎意料簡潔:
function App(props) {
const [counter, setCounter] = useState(0);
useEffect(() => {
setCounter((counter) => 10);
return () => {
setCounter((counter) => 0);
};
}, []);
return (
<h1 onClick={() => setCounter((counter) => counter + 1)}>
Hello, {props.name}
{counter} times
</h1>
);
}
多出來的只是一段如下的代碼:
useEffect(() => {
// componentDidMount is here!
setCounter((counter) => 10);
return () => {
// componentWillUnmount is here!
setCounter((counter) => 0);
};
}, []);
第一個**setCounter(counter=>10)**是隨我們Welcome一起載入運行,而在return的function則是此effect的 teardown logic,也就是為清理
資源而寫的。你可能會問,那麼這段邏輯取代了 componentDidMount
、componentDidUpdate
、componentWillUnmount
那個呢?
- 原本應在
componentDidMount
的是 setCounter(counter=>10) - 原本應在
componentWillUnmount
的是 setCounter(counter=>0)
那componentDidUpdate
呢? 有趣的是,上面並沒有對應 componentDidUpdate
的地方,因為在useEffect
第二個參數,有一個empty
array。第二個參數是此effect的dependency,React會儲起每次這個array的數值,如果在下一次update的時候發現這個array改變了,就代表了
這個effect需要重新運行。由於一個empty array永遠都是一樣,所以我們這個useEffect只會運行一次!如果沒有了第二個參數,那就變成了
componentDidUpdate
+componentDidMount
了。
useEffect(() => {
// componentDidMount is here!
// componentDidUpdate is here!
setCounter((counter) => 10);
return () => {
// componentWillUnmount is here!
setCounter((counter) => 0);
};
});
有了dependency 這個參數,可以輕易做到相當reactive的UI,例如僅當props改變,這個effect才會跟著改變,我們可以輕易寫成這個樣子
useEffect(() => {
// componentDidMount is here!
// componentDidUpdate is here!
setCounter((counter) => 10);
return () => {
// componentWillUnmount is here!
setCounter((counter) => 0);
};
}, [props]);
請留意第二個參數需要一個array。 以下是一個在Github Pages的live Example:
UseReducer
React Hooks
當然不只useState
及useEffect
兩個方法,還有一個備受注目的,就是useReducer
。 useReducer
是React團隊方便開
發者使用reducer pattern而加入。
要使用useReducer
,寫法也是非常簡單。
function counterReducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { counter: state.counter + 1 };
default:
return { counter: 0 };
}
}
function App(props) {
const [state, dispatch] = useReducer(counterReducer, { counter: 10 });
return (
<h1 onClick={() => dispatch({ type: 'INCREMENT' })}>
Hello, {props.name}
{state.counter} times
</h1>
);
}
上面 counterReducer
是正常reducer的寫法,只有一個簡單action
,就是INCREMENT
。 useReducer
會return兩個數值,第一個就
是state,第二個就是對應reducer的dispatch function,所以當一運行dispatch({type:"INCREMENT"})
時,就會把counter加了1。
此段代碼於上面CodeSandbox裏面的index-reducer.js
之內。
總結
useEffect
及useReducer
都大大簡化了React寫複雜代碼的難度。當然順帶一提的時, React Hooks
還是處於實驗性質的功能,各位尚未可以
應用於現實世界的軟件上的。不過由於功能强大,有取代現有複雜框架之勢,相當可能廣泛使用。