iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 12
0
Modern Web

React + GraphQL 全端練習筆記系列 第 12

Redux 簡介

本系列文以製作專案為主軸,紀錄小弟學習React以及GrahQL的過程。主要是記下重點步驟以及我覺得需要記憶的部分,有覺得不明確的地方還請留言多多指教。

Redux 的用途?

集中管理App的state。

React 的組件構成主要是樹狀架構,並且State主要是由親傳子的方向流動,
所以當某個Componet需要更新的State落在另一棵樹上的時候,麻煩就來了。

一般的處理是用提升State的作法,把該State拉升到兩棵樹的共用根源,然侯在從那兒下傳到兩個需要該State的Component上。

以下圖為例,若需要用B來更新某個在A上的State,就把那個State改寫在根源組件上,回傳state與setState方法給A、B兩部件使用。

當這種情形一多,你的根組件就會長滿一堆state與method。而且往下傳遞props時會路過許多不需要這些props的組件,多很多累贅的props敘述。

React有提供一些方法可以減輕這種狀況的,像是前面介紹的composition(跳過子組件傳遞props給孫組件),或是使用Context

另外就是使用像Redux這類套件,把Store的管理從樹狀構造中獨立出來,集中管理,並讓每個component從這個獨立store取用需要的state跟更新方法。

Redux 基本構成

要創建Redux store需要三元素:

  • states
    專案的唯一資訊源。

  • actions
    給store的指令,讓store根據action執行相應的動作。

    先看action的範例:

    const addTodoAction = {
      type: 'todos/todoAdded',
      payload: 'Buy milk'
    }
    

    一個action就是個純javacript物件,而action慣例需要一個 type,幫助reducer辨識action後決定如何更新state。

    action type建議使用固定字串,方便管理。

    除了type外action可以包含額外的資訊(範例的payload),像是使用者輸入的字串等等,這部分命名、資料種類不拘。

    一般不會把action寫死,而是用action creator根據輸入產生需要的action:

    const addTodo = text => {
      return {
        type: 'todos/todoAdded',
        payload: text
      }
    }
    
  • reducers
    左手是目前的state,右手是action,雙掌合十鍊成新的state。
    白話: 接收action,根據目前的state產出新的state。

    範例:

    const initialState = { value: 0 }
    
    function counterReducer(state = initialState, action) {
      // Check to see if the reducer cares about this action
      if (action.type === 'counter/increment') {
        // If so, make a copy of `state`
        return {
          ...state,
          // and update the copy with the new value
          value: state.value + 1
        }
      }
      // otherwise return the existing state unchanged
      return state
    }
    

    在定義reducer的時候可以賦予state初始值,沒有的話state就是undefined。

    每當store 收到action的時候,就會轉送給reducer,後者可以用任何方式回傳新的state,必須回傳。

    一般是根據action的type用switch決定如何回傳新的state:

    function todoApp(state = initialState, action) {
      switch (action.type) {
        case SET_VISIBILITY_FILTER:
          return Object.assign({}, state, {
            visibilityFilter: action.filter
          })
        case ADD_TODO:
          return Object.assign({}, state, {
            todos: [
              ...state.todos,
              {
                text: action.text,
                completed: false
              }
            ]
          })
        default:
          return state
      }
    }
    

    注意Redux觸發畫面更新的機制與React相同: 比對新舊state差異,所以reducer中不該直接對state做變更(immutable),而是拷貝目前state值後對拷貝的物件作變更後回傳。

Redux 基本運作

建立Redux架構的流程大致如下:

創建 Reducer

建立reducer,當中就包含對應的action、以及當沒有action時回傳的初始state。

    const initialState = { value: 0 }
    
    function counterReducer(state = initialState, action) {
      // Check to see if the reducer cares about this action
      if (action.type === 'counter/increment') {
        // If so, make a copy of `state`
        return {
          ...state,
          // and update the copy with the new value
          value: state.value + 1
        }
      }
      // otherwise return the existing state unchanged
      return state
    }

創建 Store

建立好reducer後,用於定義store:

import { configureStore } from '@reduxjs/toolkit'

const store = configureStore({ reducer: counterReducer })
console.log(store.getState())

用configureStore初始化store設定,會在沒有指定action的狀態下將reducer物件跑一遍後收到的default return作為state初始值。

創建好之後可以用store.getState()取得整個存在store中的state物件。

Store 執行 action

store建立好後就能用store.dispatch()執行想要的action。

const IncreamentAction = { type: 'counter/increment' }
store.dispatch(IncreamentAction)

以上是一步步定義的步驟,顯得有些瑣碎,不過實務上用redux toolkit建立splice集中管理action、reducer的關聯,會更易理解與管理,這部分在接下來的實作會操作。

References:


上一篇
SASS/SCSS 簡介
下一篇
仿Trello - 建立 Redux Store
系列文
React + GraphQL 全端練習筆記30

尚未有邦友留言

立即登入留言