Previous Page
cover
Reactjs
Redux
React Hooks
Author: Edison Chue 2021-07-25 10 mins read

React.js筆記:React Redux Hooks

在React Redux v7.1.0中首次加入了屬於React Redux的custom Hooks API,個人認爲比原來的connect()、mapStateToProps等Function好理解多,要使用相關套件前記得都要確認版本。

npm install react-redux@7.1.0

_2021-04-24_18.04.17.png

以上圖簡單情景爲例,建立單純的Reducer,資料都是直接Hard code:

// src/reducers/index.js
import { combineReducers } from "redux";

const songsReducer = () => {
  return [
    {
      title: "Bitchin' Summer",
      artist: "Avril Lavigne",
      duration: "3:32",
      favourite: false,
    },
    {
      title: "勘冴えて悔しいわ",
      artist: "ずっと真夜中でいいのに。",
      duration: "4:03",
      favourite: true,
    },
    {
      title: "絶対にチョコミントを食べるアオイチャン",
      artist: "GYARI",
      duration: "5:56",
      favourite: false,
    },
    {
      title: "夜明けの歌",
      artist: "M2U x ダズビー",
      duration: "3:16",
      favourite: true,
    },
  ];
};

const selectedSongReducer = (selectedSong = null, action) => {
  if (action.type === "SONG_SELECTED") return action.payload;
  return selectedSong;
};

export default combineReducers({
  songs: songsReducer,
  selectedSong: selectedSongReducer,
});

然後跟Class-based的React Redux一樣,src目錄中的index.js需要import Provider和createStore並以Provider tag包住App Component:

// src/index.js
import React from "react";
import { render } from "react-dom";
import { Provider } from "react-redux";
import { createStore } from "redux";

import App from "./components/App";
import reducers from "./reducers";

render(
  <Provider store={createStore(reducers)}>
    <App />
  </Provider>,
  document.querySelector("#root")
);

Action creator,需要配搭dispatch方法更新Store存放的State:

// src/actions/index.js
// Action creator
export const selectSong = (song) => {
  // Return an action
  return {
    type: "SONG_SELECTED",
    payload: song,
  };
};

基本的配置完成!以上的做法都跟Class-based的React Redux一模一樣。

在情景中我們還需要建立App Component以及SongList Component,App Component只負責統整所有Component再交給ReactDOM render,故此也程式碼跟最基本的Functional Component沒差多少,會有nested div以及className等只是因爲此例以Semantic UI作爲Styling:

// src/components/App.js
import React from "react";
import SongList from "./SongList";

const App = () => {
  return (
    <div className="ui container grid">
      <div className="ui row">
        <div className="column eight wide">
          <SongList />
        </div>
      </div>
    </div>
  );
};

_2021-04-25_00.35.11.png

useSelector以及useDispatch

比較需要說明的是SongList Component,以React Redux v7.1.0的useSelector可以簡單地將Component需要的state從Store中取出來。不再需要經過connect() 方法,所以mapStateToProps方法也不用寫了;因爲這邊只有songs和selectedSong兩個state,我們不使用destructor直接提取state也是可以:

const SongList = () => {
    const state = useSelector((state) => state);
  console.log(state);
    ...
}

這裏我們把state console.log出來看看:

_2021-04-25_00.15.12.png

出來的結果跟使用mapStateToProps一樣,但程式碼短得多了!

_2021-04-24_17.56.57.png

同樣新版本也不再需要以mapDispatchToProps + dispatch方法來觸發Reducer,React Redux v7.1.0提供了Hooks API(useDispatch)來直接回傳dispatch方法,此例我們的button onClick事件便會呼叫dispatch來觸發Reducer

const dispatch = useDispatch();
...
// 
<button
    onClick={() => {
        // dispatch an action
        dispatch(selectSong(song));
  }}
    className="ui right floated button primary"
>
    Select
</button>

_2021-04-25_00.29.11.png

按下Button便會促使SongList Component重新render,可以透過console.log看到state中的selectedSong已經更新成所選的歌曲:

_2021-04-25_00.29.59.png

簡單兩個Hooks ,就能完成提取Store中的state和觸發Reducer,程式碼還更簡潔直覺。

完整程式碼:

import React from "react";
import { useSelector } from "react-redux";
import { useDispatch } from "react-redux";
import { selectSong } from "../actions";

const SongList = () => {
  // useSelector: essentially identical to the mapStateToProps function
  // which extract the Redux store's state to this component props
  // using the useSelector Hook eliminated the need of connect()
  const state = useSelector((state) => state);
  console.log(state);
  const dispatch = useDispatch();

  const renderList = () => {
    return state.songs.map((song) => {
      // return some jsx element
      return (
        <div className="item" key={song.title}>
          <div className="content">
            <button
              onClick={() => {
                // dispatch an action
                dispatch(selectSong(song));
              }}
              className="ui right floated button primary"
            >
              Select
            </button>
            {song.favourite && <i className="yellow star icon"></i>}
            {song.title}
          </div>
        </div>
      );
    });
  };

  return <div className="ui divided items">{renderList()}</div>;
};

export default SongList;

搞懂React、Redux、Middleware:

100 行秒懂 React、Redux、Middleware

Day 27: Redux篇 - 使用react-redux綁定Redux與React - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天