react redux 二次開發流程

  • 2019 年 10 月 5 日
  • 筆記

在一個大項目中如何引入redux及其相關技術棧(reactredux reduxthunk reduximmutable ),已經成為react前端工程師不可或缺的技能,下面通過實現一個簡單的todolist效果,來介紹相關流程

 

 

1.引入redux進行應用數據管理,安裝相關依賴

yarn add redux  react-redux redux-thunk redux-devtools-extension

一般目錄結構

 

2.創建好store.js、reducer.js、action.js、action-types.js

1)store.js

1 /*  2 redux最核心的管理對象store  3  */  4 import {createStore} from 'redux'  5 import reducer from './reducer'  6  7 const  store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())  8 // 向外默認暴露store  9 export default  store

 2) reducer.js

 1 import {CHANGEINPUT,ADDITEMS } from './action-types'   2   3 const defalutState = {   4     inputValue : 'wuxiaohui',   5     list :[]   6 }   7   8 export default (state = defalutState,action) =>{   9     if(action.type === CHANGEINPUT){  10         let newState = JSON.parse(JSON.stringify(state))//深拷貝  11         newState.inputValue = action.value  12         return newState  13     }  14     if(action.type === ADDITEMS){  15         let newState = JSON.parse(JSON.stringify(state))  16         newState.list.push(newState.inputValue)  17         newState.inputValue = ''  18         return newState  19     }  20  21     return state  22 }

3)action.js

import {CHANGEINPUT,ADDITEMS } from './action-types'    export const inputChange = (e)=>({      type:CHANGEINPUT,      value:e.target.value  })    export const clickButton = ()=>({      type:ADDITEMS  })

4)action-types.js

/*  包含n個action type常量名稱的模組   */  export const CHANGEINPUT = 'change_input'  export const ADDITEMS = 'add_item'

3.創建todolistui組件

編寫TodolistUI.js,由於沒有雙向綁定,通過onChange的inputChange事件拿到輸入值並通過inputValue傳回給輸入框,clickButton則是向list中追加輸入框中輸入的數據,輸入後清空。該邏輯在 reducer.js中體現,UI組件只負責展示。
 
//把TodoList改為UI組件-提高性能    import React from "react";     const TodoListUI =(props)=>{  // 接收connect連接器映射傳遞的屬性和函數      let {inputValue ,inputChange,clickButton,list} = props;      return (            <div>              <div>                  <input value={inputValue} onChange={inputChange} />                  <button onClick={clickButton}>提交</button>              </div>              <ul>                  {                      list.map((item,index)=>{                          return (<li key={index}>{item}</li>)                      })                  }              </ul>          </div>      );  }  export  default TodoListUI

4.引入react-redux進行應用數據管理

1)總入口中index.js中引入react-redux和容器組件APP
react-redux的核心:Provider(用於入口) 和 connect(用於數據和函數映射)
使用provider
/*  入口js   */  import React from 'react';  import ReactDOM from 'react-dom';    import App from './containers/App';  import { Provider} from 'react-redux'  import store from './redux/store'    //<Provider>是一個提供器,只要使用了這個組件,組件裡邊的其它所有組件都可以使用store了  //聲明一個App容器組件,然後這個組件用Provider進行包裹。  const  AppList = (      <Provider store={store}>          <App />      </Provider>  )  ReactDOM.render(AppList, document.getElementById('root'));

2)connect連接器(連接UI組件和redux中的action.js方法)成為容器組件
connect-連接器用來將redux管理的state數據映射成UI組件的一般屬性(如輸入框的值)

connect-連接器用來將redux管理的包含diaptch程式碼的函數映射成UI組件的函數屬性的函數

1.在redux目錄中的action.js定義UI組件要調用的方法,然後編寫好reducer的業務邏輯
2.在containers容器APP組件中 引入UI組件TodolistUI和action進行連接

import React from 'react'  import {connect} from 'react-redux'    import TodoListUI from '../components/TodoListUI'  import {inputChange,clickButton} from '../redux/actions'    /*  connect-連接器用來將redux管理的state數據映射成UI組件的一般屬性(如輸入框的值)   指定向TodoList傳入哪些一般屬性(屬性值的來源就是store中的state)   */  const stateToProps = (state)=>{      return {          inputValue : state.inputValue,          list:state.list      }  }    /*  connect-連接器用來將redux管理的包含diaptch程式碼的函數映射成UI組件的函數屬性的函數  (如輸入的onChange事件)  可以寫多個函數,用逗號隔開   */  // 寫法1  // const dispatchToProps = (dispatch) =>{  //     return {  //         inputChange(e){  //             //派發action到store中:定義action 然後派發  //             //派發後就在reducer裡邊,編寫對應的業務邏輯了  //             let action = {  //                 type:'change_input',  //                 value:e.target.value  //             }  //             dispatch(action)  //         },  //         clickButton(){  //  //             let action = {type:'add_item'}  //             dispatch(action)  //         }  //     }  // }  //export default connect(stateToProps,dispatchToProps )(TodoListUI);    // 寫法2  export default connect(stateToProps,{inputChange,clickButton} )(TodoListUI);

5.引入 immutablejs
首先,我們有必要來劃分一下邊界,哪些數據需要使用不可變數據,哪些數據要使用原生js數據結構,哪些地方需要做互相轉換
  • 在redux中,全局state必須是immutable的,這點毋庸置疑是我們使用immutable來優化redux的核心
  • 組件props是通過redux的connect從state中獲得的,並且引入immutableJS的另一個目的是減少組件shouldComponentUpdate中不必要渲染,shouldComponentUpdate中比對的是props,如果props是原生JS就失去了優化的意義
  • 組件內部state如果需要提交到store的,必須是immutable,否則不強制
  • view提交到action中的數據必須是immutable
  • Action提交到reducer中的數據必須是immutable
  • reducer中最終處理state必須是以immutable的形式處理並返回
  • 與服務端ajax交互中返回的callback統一封裝,第一時間轉換成immutable數據

1)安裝相關依賴

yarn add immutable  redux-immutable 

2)在reducer中 immutable的fromJs,把defalutState 轉為immutable數據

 1 // 引入fromJS 將state數據轉變為 immutable對象   2 const defalutState = fromJS({   3     inputValue : 'wuxiaohui',   4     list :[]   5 });   6   7 //immutablejs的相關介面——使用get 和set 方法來改變state   8 export default (state = defalutState,action) =>{   9     if(action.type === CHANGEINPUT){  10         // let newState = JSON.parse(JSON.stringify(state)) //深拷貝  11         // newState.inputValue = action.value  12         // return newState  13         return  state.set('inputValue',action.value)  14     }  15     if(action.type === ADDITEMS){  16         // let newState = JSON.parse(JSON.stringify(state))  17         // newState.list.push(newState.inputValue)  18         // newState.inputValue = ''  19         // return newState  20  21         return state.merge({  22             'list': state.get('list').push(state.get('inputValue')),  23             'inputValue': ''  24         });  25  26     }  27  28     return state  29 }

3)在容器組件中App.js中映射時使用get獲取相關屬性值

 1 /*   2 connect-連接器用來將redux管理的state數據映射成UI組件的一般屬性(如輸入框的值)   3  指定向TodoList傳入哪些一般屬性(屬性值的來源就是store中的state)   4  */   5 const stateToProps = (state)=>{   6     return {   7         // inputValue : state.inputValue,   8         // list:state.list   9         //因為引入了immutable,state 已變為不可變對象只能調用get或set方法  10         inputValue : state.get('inputValue'),  11         list:state.get('list')  12     }  13 }

更多用法:
 

4)redux-immutable在reducer的處理

combineReducers(reducers)
隨著應用變得越來越複雜,可以考慮將 reducer 函數 拆分成多個單獨的函數,拆分後的每個函數負責獨立管理 state 的一部分

類似這樣

 1 import { combineReducers } from 'redux';   2 import { reducer as headerReducer } from '../common/header/store';   3 import { reducer as homeReducer } from '../pages/home/store';   4 import { reducer as detailReducer } from '../pages/detail/store';   5 import { reducer as loginReducer } from '../pages/login/store';   6   7 const reducer = combineReducers({   8    header: headerReducer,   9    home: homeReducer,  10    detail: detailReducer,  11    login: loginReducer  12 });  13  14 export default reducer;

 

假如我們的reducer在header中,組件中獲取數據時,用get方法

const mapStateToProps = (state) => {      //inputValue是immutable對象,不能用state.header.inputValue的形式獲取,要用get()        return  {            inputValue :state.header.get('inputValue'),            list:state.header.get('list')        }  }

在使用了redux-immutable

1 //combineReducers不再用rudux里的,而是redux-immutable里的,這樣combineReducers里的對象就是一個immutable對象  2 //import {combineReducers} from 'redux'  3 import {combineReducers} from 'redux-immutable'  4 import {reducer as headerReducer} from '../common/header/store'  5 const reducer=combineReducers({  6     header:headerReducer  7 });  8 export default reducer;

獲取數據的時候用get(),或者getIn()–獲取結構化數據

1 const mapStateToProps = (state) => {  2       return  {  3           //inputValue :state.header.get('inputValue'),  4          // list:state.header.get('list')   5              inputValue :state.getIn(['header','inputValue']),  6              list:state.getIn(['header','list'])  7       }  8 }

 
流程中例子詳見GitHub