跳至主內容

到底React Hooks有何特別(二)?淺談useEffect及useReducer

到底React Hooks有何特別(二)?淺談useEffect及useReducer
Gordon Lau 劉偉中
2018-11-29

於本篇文章的上集,我們討論了useState如何令Stateful React component簡化良多,此篇主要討論的是如何使 用useEffectuseEffect可以簡化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

此例子中並無諸如componentDidUpdatecomponentDidMountcomponentWillUnmount等方法,React Hooks到底如何有效於 function 中取代呢?答案就是運用 useEffect。 舉例加上componentDidMountcomponentWillUnmount,來初始化及重置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,也就是為清理 資源而寫的。你可能會問,那麼這段邏輯取代了 componentDidMountcomponentDidUpdatecomponentWillUnmount 那個呢?

  • 原本應在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當然不只useStateuseEffect兩個方法,還有一個備受注目的,就是useReduceruseReducer是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,就是INCREMENTuseReducer會return兩個數值,第一個就 是state,第二個就是對應reducer的dispatch function,所以當一運行dispatch({type:"INCREMENT"})時,就會把counter加了1。

此段代碼於上面CodeSandbox裏面的index-reducer.js之內。

總結

useEffectuseReducer 都大大簡化了React寫複雜代碼的難度。當然順帶一提的時, React Hooks還是處於實驗性質的功能,各位尚未可以 應用於現實世界的軟件上的。不過由於功能强大,有取代現有複雜框架之勢,相當可能廣泛使用。

留言

閱讀更多

到底React Hooks 有何特別?

到底React Hooks 有何特別?

到底React Hooks 有何特別?
Gordon Lau 劉偉中
2018-11-27

新近推出的React 16.7包括一個很有趣的功能,名字叫做React Hooks。看到這個名字,很多人會下意識認為是在講componentDidMount, componentDidUpdate等方法。但其實這些方法的正名是 React Lifecycle Method, 推出React Hooks是為了方便開發者多用functional component,但仍然能夠使用state及 props等重要功能。


好Programmer是怎樣煉成的?

好Programmer是怎樣煉成的?

好Programmer是怎樣煉成的?
Gordon Lau 劉偉中
2018-12-20

有一個大部份僱主都面對的難題,在芸芸履歷之中,如何萬中挑一,找到好programmer呢?聘請程式設計師很難,不像其他行業,打開 履歷就一目了然:有時履歷上滿滿証書的,其實連FizzBuzz也寫不了;有時看起來像個fresh graduate的,卻又有無限潛力。 如果你是一個要聘請程式設計師的僱主,你應該如何是好呢?


軟件工程及軟件工藝

軟件工程及軟件工藝

軟件工程及軟件工藝
Gordon Lau 劉偉中
2018-12-24

軟件工程是一個相當耳熟能詳的名詞,軟件工程(Software Engineering)由來已久,亦因此程式設計師(Programmer)又稱為軟件工程 師(Software Engineer)。


Web Technology為何征服世界?

Web Technology為何征服世界?

Web Technology為何征服世界?
Gordon Lau 劉偉中
2019-02-05

2007年,蘋果宣佈發佈第一代iPhone,標誌智能電話時代的開始;一年之後Android亦宣告面世,從此時起,智能電話的發展迅速,Mobile App成為軟件的代 名詞,筆者初初成為軟件工程師時,總有朋友詢問我是否正在開發Mobile App,縱使筆者的專業一直都是網頁及後端開發之上。而其時亦有不少預測,預測[網站將會被Mobile App完全取代](https://searchenginewatch.com/sew/opinion/2414336/the-final-hurdle-is-cleared-apps-will-replace-websites)。網站所用的HTML、CSS、JS等,亦將成為歷史,送入博物館之內。


索取課程大綱
提交後, 請檢查你的電郵
hello@tecky.iot.me/tecky_hub+852 9725 6400
green_org
商界展關懷 2019-2022
英國頒證機構 TQUK 認可中心
aws_partner
薯片叔叔共創社 重塑教育挑戰大獎
B Corp™ 認證共益企業
無障礙網頁內容指引 (WCAG) 2.1 AA 級
香港無障礙網頁 金獎
© 2024 Tecky Academy Limited