1. 程式人生 > >react redux 二次開發流程

react redux 二次開發流程

在一個大專案中如何引入redux及其相關技術棧(react-redux redux-thunk redux-immutable ),已經成為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 }
更多用法: 參考https://blog.csdn.net/weixin_33728268/article/details/87972634  

4)redux-immutable在reducer的處理

combineReducers(reducers) https://cn.redux.js.org/docs/api/combineReducers.html 隨著應用變得越來越複雜,可以考慮將 reducer 函式 拆分成多個單獨的函式,拆分後的每個函式負責獨立管理 state 的一部分 參考文件 https://www.cnblogs.com/superlizhao/p/9474859.html

類似這樣

 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 https://github.com/scalerone/ReduxDemo

&n