iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 4
3
Modern Web

Next.js + 各種套件組合系列 第 4

Next.js & Redux and Mobx

介紹

這兩套都是在React上常看見的 Global State Management,在REACT 因為是透過組件在拼裝,使用state與 props 在取值 ,如果有跨三層以上的拼裝在取值會變得非常不容易,所以需要 Global State Management 來管理,也方便每個元件都可以快速的溝通

next with redux
Redux 起手式就是創建一個 Store,以下就是一個基本的 createStore 起手式
crateStrore 顧名思義 就是創建 Store 的方法,他需要基本一個 Reducer(State) 跟 REDUCER 的 Init 預設值
(REDUCER就是這個 STATE長的樣子,多個的話要用COMBINE組成一個)

composeWithDevTools 則是分析 applyMiddleware
目前看到的是使用最簡單的是 ThunkMiddleware,之後進階可以使用 Redux-Saga,Redux-Observable 等等的 MIDDLEWARE

這些 MiddleWare 都是為了處理 Side Effect,目前推薦 Redux-Observable 的原因是 Thunk 只能發 Request 並不能處理比較進階例如取消的動作,Redux-Saga 提供了許多的 Effect 去管理這些 Request 與 Dispatch 等等 但是資料夾以及流程上要撰寫許多程式碼維護起來比較大,目前進化到REDUX-OBSERVABLE 可以使用 Rx.js 去管理這些 Side Eeffect 程式碼短好維護 可讀性高
這是 MiddleWare 的選擇建議,如果是初學 Redux 可以先使用 Thunk 入門


import { createStore, applyMiddleware } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import thunkMiddleware from 'redux-thunk'
 
export const initStore = (initialState = exampleInitialState) => {
  return createStore(reducer, initialState, composeWithDevTools(applyMiddleware(thunkMiddleware)))
}

以上為 Redux 基本的 Store 設定

接下來看一下 Reducer 的樣貌,基本上Reducer 可以有多個但是最終都要透過 COMBINER 結合放在 Store 底下

// 初始值initialState
const exampleInitialState = {
  lastUpdate: 0,
  light: false,
  count: 0
}
// reducer
export const reducer = (state = exampleInitialState, action) => {
  switch (action.type) {
    case actionTypes.TICK:
      return Object.assign({}, state, { lastUpdate: action.ts, light: !!action.light })
    case actionTypes.ADD:
      return Object.assign({}, state, {
        count: state.count + 1
      })
    default: return state
  }
}

action 跟 dispatch的作用,在 Redux 中也常會看到這兩個名詞,來解說一下
1.action 在 Reducer 裡看到 switch action.type 符合的 action 準備對STATE做動作,單純就只是FITER使用
2.dispatch 就是先定義好一個準備被觸發的FUNCTION 裡面就包含 action 種類以及通常還有 payload (要傳送的資料)在對應到 switch action.type ,通常SIDE EFFECT middlewave 也會在這裡根據 action 來處理

action 定義 長的樣子


export const actionTypes = {
  ADD: 'ADD',
  TICK: 'TICK'
}

dispatch 定義 會長這個樣子 不立即觸發

export const startClock = () => dispatch => {
  return setInterval(() => dispatch({ type: actionTypes.TICK, light: true, ts: Date.now() }), 800)
}

export const addCount = () => dispatch => {
  return dispatch({ type: actionTypes.ADD })
}

Next.js 中使用 redux 因為有 Server Side Render (SSR) 如果需要在SERVER 端先跑 Render 的話可以先定義 Server 的 Dispatch ,也可以共用 Dispatch 再由參數判斷是否是 SSR 再去動作前後端

export const serverRenderWork = () => dispatch => {
  return dispatch({ type: actionTypes.TICK, light: 'Server' })
}

在 Next.js 的 GgetInitialProps 判斷是否需要 SSR 先 dispatch

static getInitialProps ({ store, isServer }) {
    if(isServer){
        store.dispatch(serverRenderWork())
    }    
    return { isServer }
  }

在 Next.js 中要注意的是 import withRedux from 'next-redux-wrapper'
這個因為他有幫忙做到 Singleton Store 前後端一致 其他在使用跟一般都差不多

next with mobx
Mobx 使用 ES7的 Decorators 使用前要先安裝一下 Babel 先新增一個.babelrc


{
  "presets": [
    "next/babel"
  ],
  "plugins": [
    "transform-decorators-legacy"
  ]
}

Mobx 使用裝飾子 @observable宣告 是透過觀察變數
action 也是使用裝飾子 @action 定義 如下範例
@就是裝飾子 (就是透過HIGT ORDER COMPONENT HOC 增加一些 Props )

import { action, observable } from 'mobx'

let store = null

class Store {
  @observable lastUpdate = 0
  @observable light = false

  constructor (isServer, lastUpdate) {
    this.lastUpdate = lastUpdate
  }

  @action start = () => {
    this.timer = setInterval(() => {
      this.lastUpdate = Date.now()
      this.light = true
    })
  }

  stop = () => clearInterval(this.timer)
}

如果在子元件要使用 action 只需要 @inject('store') 就可以透過 Props 使用


import React from 'react'
import Link from 'next/link'
import { inject, observer } from 'mobx-react'

@inject('store') @observer  //<---只需注入就可以透過 props 拿到 store 的 action 跟變數
class Page extends React.Component {

這邊的 Props 就可以使用 action 了
以下省略

Next.js server.js部分 如果是要用 Mobx
const mobxReact = require('mobx-react')

mobxReact.useStaticRendering(true) 這個要設定為true

Next.js & Mobx SSR 因為生命週期渲染得原因避免一些問題 要記得 Server Part要用 mobxReact.useStaticRendering(true)

https://github.com/mobxjs/mobx-react
參考文章
When using server side rendering, normal lifecycle hooks of React components are not fired, as the components are rendered only once. Since components are never unmounted, observer components would in this case leak memory when being rendered server side. To avoid leaking memory, call useStaticRendering(true) when using server side rendering. This makes sure the component won't try to react to any future data changes.

總結

Redux 比 Mobx 設定比較多且複雜 action dispatch Reducer store middleware 但架構比較嚴僅
反之Mobx 比較容易使用

在 Next.js 之中 Redux 使用上也並無差異太多 主要都在 getInitialProps Redux 要決定是 SSR 時是否 dispatch 另外要使有前後端 store Singleton 記得要用 next-redux-wrapper

Mobx 基本上真的很簡單使用 定義好注入就可以用了 SSR 要注意 設定 useStaticRendering 為 true

官方參考 github
https://github.com/zeit/next.js/tree/canary/examples/with-redux
https://github.com/zeit/next.js/tree/canary/examples/with-mobx


上一篇
Next.js & Layout
下一篇
Next.JS & Firebase Auth
系列文
Next.js + 各種套件組合30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
hannahpun
iT邦新手 4 級 ‧ 2018-12-13 12:35:09

超感謝 我是新手 找redux-devtools-extension方法找好久還好有你的文章

polo iT邦新手 4 級 ‧ 2018-12-17 15:16:49 檢舉

^^

我要留言

立即登入留言