{ PH_Dev }

Published on

React Redux 與 Hooks

Authors
  • avatar
    Name
    Penghua Chen(PH)
    Twitter

在版本 7.1 之後, React Redux 也加入了 Hooks,簡化了以往需要透過 connect 方法並傳入 mapStateToPropsmapStateToDispatch 的方式才可以操作 Redux 的 store 物件。

而今天要來看看怎麼使用,也會看看在文件中的定義。

使用 Hooks 的前置作業

在使用 Hooks 之前需要先完成一些前置作業,這個部分其實也很簡單,就是建立一個 store 並將 store 物件透過 Provider 提供給 App 元件使用。

這邊參考官方提供的範例,基本上相差不多:

const store = createStore(rootReducer)

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

useSelector

接著第一個要學習的是 useSelector這個方法允許我們直接從 Redux store 中的狀態提取數據到元件中。

透過 useSelector 可以取代掉 mapStateToProps 的使用方式

在之前如果我們需要從 store 中拿到某個狀態的數據會是這麼寫:

// ...略
const mapStateToProps = (state) => {
  return {
    counter: state.counter
  };
};

export default connect(mapStateToProps)(App)

但有了 Hooks,我們可以改寫成這樣:

import React from 'react'
import { useSelector } from 'react-redux'

export const CounterComponent = () => {
  const counter = useSelector(state => state.counter)
  return <div>{counter}</div>
}

而在使用上基本上 mapStateToProps 大致相同於 useSelector,但依然有些差異,以下擷取至文件中的段落:

  • useSelector 可以回傳任何值,並不一定是一個物件(mapStateToProps則是必定回傳一個物件)。
  • useSelector 會將前一個結果與當前的結果進行比較,如果不同就會強制更新元件,不然就不會更新元件。
  • useSelector 沒有自己的 props,但可以透過 JavaScript 的閉包觀念取得元件中的 props
  • useSelector 預設是使用 === 嚴格等於的方式檢查(mapStateToProps 則是 ==)

useDispatch

然後要學習的是 useDispatch,對於 useSelector 有了一些認識就知道這個方法要用來做什麼

沒錯,可以用來取代掉 mapStateToDispatch !!

而看到這裡應該不難發現,使用了 Hooks 之後,我們基本上就要跟 connectmapStateToPropsmapStateToDispatch 說再見啦,完全用不到了

而之前如果我們需要發一個 action 到 Redux 的 Reducer 中更新 state 的時候會是這麼寫:

// ...略
const mapDispatchToProps = (dispatch) => {
  return {
    incrementHandler: () => dispatch({ type: "INCREMENTHANDLER" })
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(App);

但現在透過 Hooks 的話,可以這麼寫:

import React from 'react'
import { useDispatch } from 'react-redux'

export const CounterComponent = ({ value }) => {
  const dispatch = useDispatch();
  
  return (
    <div>
      <span>{value}</span>
      <button onClick={() => dispatch({ type: 'INCREMENTHANDLER' }  )}>
        Increment counter
      </button>
    </div>
  )

而這邊需要注意的是在文件中有提到如果我們是透過 callback 的方式來發一個 action 時,需要透過 useCallback 的方式處理,避免掉多餘的重新渲染。

所以我們把上面的程式碼修改如下:

import React from 'react'
import { useDispatch } from 'react-redux'

export const CounterComponent = ({ value }) => {
  const dispatch = useDispatch();
  
  const incrementHandler = useCallback(
   () => dispatch({ type: 'INCREMENTHANDLER' }),
   [dispatch]
  );
  
  return (
    <div>
      <span>{value}</span>
      <button onClick={() => incrementHandler )}>
        Increment counter
      </button>
    </div>
  )
}

useStore

最後要學的是 useStore 這個方法,這個方法可以讓我們取得我們透過 Provider 提供的 store 物件。

來看看官方提供的程式碼:

import React from 'react'
import { useStore } from 'react-redux'

export const CounterComponent = ({ value }) => {
  const store = useStore()

  // EXAMPLE ONLY! Do not do this in a real app.
  // The component will not automatically update if the store state changes
  return <div>{store.getState()}</div>
}

改寫測試範例

而在篇幅的最後,這裡要改寫第23天的測試範例

這邊擷取出局部程式碼:

相關測試範例,點擊前往

import React, { useCallback } from "react";
import { useSelector, useDispatch } from "react-redux";
import "./styles.css";

const App = () => {
  const dispatch = useDispatch();
  const number = useSelector((state) => state.number);

  const incrementHandler = useCallback(
    () => dispatch({ type: "INCREMENTHANDLER" }),
    [dispatch]
  );

  const decrementHandler = useCallback(
    () => dispatch({ type: "DECREMENTHANDLER" }),
    [dispatch]
  );

  const incrementTenNumberHandler = useCallback(
    (number) =>
      dispatch({
        type: "INCREMENTTENNUMBERHANDLER",
        payload: { number }
      }),
    [dispatch]
  );

  return (
    <div className="App">
      <h2>使用 React Redux Hooks 改寫</h2>
      <h2>數字: {number}</h2>
      <button onClick={incrementHandler}>點擊 + 1</button>
      <button onClick={decrementHandler}>點擊 - 1</button>
      <button
        onClick={() => {
          incrementTenNumberHandler(number);
        }}
      >
        由當前數字 + 10
      </button>
    </div>
  );
};

export default App;

上面的測試範例是簡單的加減計數器,這裡透過 useSelector 取得 store 物件中的 number 狀態的數據,並透過 useDispatchdispatch 對應的 action 到 Reducer 中,並執行後續的 state 更新。

鐵人賽文章與程式碼同步發佈於:

資源