Redux在React Hook中的使用及其原理

Hello, 各位勇敢的小夥伴, 你們好, 我是大家的嘴強王者小五, 身體健康, 腦子沒病.
 

本人有豐富的脫髮技巧, 能讓你一躍成爲資深大咖.react

一看就會一寫就廢是本人的主旨, 菜到摳腳是本人的特色, 卑微中透着一絲絲剛強, 傻人有傻福是對我最大的安慰.web

歡迎來到小五隨筆系列Redux在React Hook中的使用及其原理.redux

淺談Redux

下面將從what, why, how to 三個方面來講說Reduxsegmentfault

第一問 what 什麼是Redux

將一個web應用拆分紅視圖層與數據層, Redux就是保存其數據的一個容器, 其本質就是維護一個存儲數據的對象.app

  • State : 一個存放數據的容器 (一個對象)
const initState = {
  count: 0,
}
  • Action : 一個 want to do 的過程 (計劃要作一個什麼樣的操做)ide

    • ActionType是對Action的描述, 也是鏈接ActionReducer的橋樑
    • 本質上是一個由ActionTypepayload(數據)組成的對象
export const increaseConstant = 'INCREASE' // ActionType

{
   type: increaseConstant,
   payload,
}  // Action
  • Reducer : 一個 to do 的過程 (執行Action計劃的操做)
case increaseConstant: // 當 ActionType 爲 'INCREASE' 時, 執行count++
  return {
    ...state,
    count: payload + 1
  }

第二問 why 爲何要使用Redux

當你不知道是否須要使用Redux的時候, 那就是不須要使用.函數

下面一組動圖很好的描述了一個應用程序的開發過程, 及什麼時候須要Redux.
圖片來源及原文連接post

  • 遊戲初期階段, 數據單向傳遞, 父傳子

image

  • 遊戲進入中期, 開始有少許非父子組件間須要通信一些數據

image

  • 遊戲進入後期, 開始須要大量的數據通信

image

  • 此時, 就是Redux的用武之地了, 使用Redux後流程以下

image

第三問 how to 怎麼使用Redux

在說使用方法以前, 咱們先來從頭至尾模擬一個Redux的過程, 只要理解原理, 使用起來那不是小菜一碟.spa

Let's go, come on baby!code

image

下面咱們就用一個簡單的計數器的例子來模擬實現一個Redux的過程:

建立一個count組件

import React, { useState } from 'react'

const CountItem = (props) => {
  const {
    count,
    increase,
  } = props

  return (
    <>
      {count}
      <button onClick={increase}>Count++</button>
    </>
  )
}

const Count = () => {
  const [count, setCount] = useState(0)

  const increase = () => {
    setCount(count + 1)
  }

  return (
    <CountItem
      count={count}
      increase={increase}
    />
  )
}

export default Count

這樣一個簡單的count組件就建立好了, 如今, 咱們想把對數據的操做單獨封裝成一個方法名爲dispatch, 傳遞的參數爲一個Action 格式: { type: xxx, payload: xxx }

封裝一個Dispatch函數

const dispatch = (action) => {
  switch(action.type) {
    case 'INCREASE':
      return action.payload + 1
    default:
      break
  }
}

改寫increase方法

const increase = () => {
-  setCount(count + 1)
+  setCount(dispatch({type: 'INCREASE', payload: count}))
}

這時, 咱們將action對象也抽離出來, 方便複用, 新建action.js.

action.js => 返回action對象

const increaseCount = (payload) => {
  return {
    type: 'INCREASE',
    payload
  }
}

改寫increase方法

const increase = () => {
-  setCount(dispatch({type: 'INCREASE', payload: count}))
+  setCount(dispatch(increaseCount(count)))
}

接下來咱們把dispatch中的事件操做抽離到reducer中, 新建reducer.js.

reducer.js => 進行數據操做

const reducer = (state, action) => {
  const { type, payload } = action
  switch(type) {
    case 'INCREASE':
      return {
        ...state,
        count: payload + 1
      }
    default:
      return state
  }
}

改寫dispatch函數

const dispatch = (action) => {
  const state = {
    count,
  }
  
  const newState = reducer(state, action)

  return newState
}

改寫increase方法

const increase = () => {
-  setCount(dispatch(increaseCount(count)))
+  setCount(dispatch(increaseCount(count)).count)
}

接下來, 咱們把set方法也拿到dispatch中, 讓全部操做都在dispatch中完成.

繼續改造dispatch函數, 增長setter作映射

const dispatch = (action) => {
  const state = {
    count,
  }
  
+  const setter = {
+    count: setCount
+  }
  
  const newState = reducer(state, action)
  
+  for (let key in newState) {
+    setter[key](newState[key])
+  }

-  return newState
}

改寫increase方法

const increase = () => {
-  setCount(dispatch(increaseCount(count)).count)
+  dispatch(increaseCount(count))
}

這裏咱們能夠看到, action.type是鏈接actionreducer的橋樑, 咱們能夠將actionType定義爲常量單獨保存.

在action中增長actionType

export const increaseConstant = 'INCREASE'

// 替換 action 和 reducer 中的 'INCREASE' 爲 increaseConstant

基於現有場景, 若是咱們有另外一個功能, 而目前的reducer並不能幫助咱們很好的把不一樣的功能劃分開來, 咱們改造一下reducer, 改形成一個對象, 用對象的key去區分功能.

改寫reducer

const reducer = {
  count(state, action) {
    const { type, payload } = action
    switch(type) {
      case increaseConstant:
        return payload + 1
      default:
        break
    }
  },
}

這時咱們要遍歷reducer, 找到正確的key, 才能讓程序正確執行, 咱們新建combineReducers.js來完成這步操做.

combineReducers

const combineReducers = (reducer) => {
  return (state, action) => {
    let ret = {}

    for (let key in reducer) {
      ret[key] = reducer[key](state[key], action)
    }
    
    return {
      ...state,
      ...ret,
    }
  }
}

繼續改下dispatch函數, 使其支持當前格式reducer.

改寫dispatch

-  const newState = reducer(state, action)
+  const newState = combineReducers(reducer)(state, action)

至此, 一個redux的實現過程就完成了, 接下來, 咱們實際用一用redux. 其實, 當完成上述操做的時候, 怎麼用就已經說的差很少了.

image

Redux + React 使用

action, reducer, Count組件同上, Count組件須要簡單改寫下.

新建store.js

import { createStore, combineReducers } from 'redux'
import reducer from './recuder'

const initState = {
  count: 0,
}

const store = createStore(
  combineReducers(reducer),
  initState,
)

export default store

新建app.jsx, 引入store

import { Provider } from 'react-redux'
import store from './store'

const App = () => {
  return (
    <Provider store={store}>
      <Count />
    </Provider>
  )
}

export default App

改寫Count組件

import React from 'react'
import { connect } from 'react-redux'
import { increaseCount } from './action'

const CountItem = (props) => {
  const {
    count,
    increase,
  } = props

  return (
    <>
      {count}
      <button onClick={increase}>Count++</button>
    </>
  )
}

const Count = (props) => {
  const {
    count,
    dispatch,
  } = props

  const increase = () => {
    dispatch(increaseCount(count))
  }

  return <CountItem count={count} increase={increase} />
}

export default connect(
  (state) => {
    return state
  },
  (dispatch) => {
    return { dispatch }
  }
)(Count)

接下來, 咱們改寫成hook的寫法

改寫Count組件

import React from 'react'
- import { connect } from 'react-redux'
+ import { useSelector, useDispatch } from 'react-redux'
import { increaseCount } from './action'

const CountItem = (props) => {
  const {
    count,
    increase,
  } = props

  return (
    <>
      {count}
      <button onClick={increase}>Count++</button>
    </>
  )
}

const Count = () => {
-  const {
-    count,
-    dispatch,
-  } = props
  
+  const count = useSelector(state => state.count)
+  const dispatch = useDispatch()

  const increase = () => {
    dispatch(increaseCount(count))
  }

  return <CountItem count={count} increase={increase} />
}

- export default connect(
-  (state) => {
-      return state
-  },
-  (dispatch) => {
-    return { dispatch }
-  }
- )(Count)

+ export default Count

至此, 本篇文章就到此結束了, 你們能夠寫個複雜一點的組件練習下, 好比todoList, 手動滑稽. 逃了逃了, 我要去timi去了, 個人小夥伴還等我帶他上白銀呢, 一幫混子, 帶不動.

image

相關文章
相關標籤/搜索