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 /* 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组件
//把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进行应用数据管理
/* 入口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\'));
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);
- 在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的处理
类似这样
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 }