react redux 二次開發流程
阿新 • • 發佈:2019-10-05
在一個大專案中如何引入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.js1 /* 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