redux 是独立的,react可以引入它来解决状态管理的问题,和 vuex 在 vue 中起的作用一样
redux 的具体过程是这样的,如下图:
步骤:
安装redux:
npm install redux –save
这次依然用 Todolist 举例
页面如下:
新建 store 文件夹
处理状态的东西放到这个文件夹下,目录如下:
getState 获取 store 中的值
todoList.js:
1 2 3 4
| import React, { Component } from 'react'; import store from '../store';
state = store.getState()
|
通过 dispatch 改变 store 中的值
todoList.js:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import React, { Component } from 'react'; import store from '../store';
<input value={ this.state.inpVal } onChange={ this.handleChange }></input>
handleChange = (e) => { const action = { type: 'CHANGE_INPUT_VAL', value: e.target.value } store.dispatch(action); }
|
reducer 中定义如何去改变 store 中的值
store/index.js, 通过引入 redux 中的 createStore 来创建一个 store
1 2 3 4 5 6 7 8 9 10
| import { createStore } from 'redux'; import reducer from './reducer';
const store = createStore( reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() );
export default store;
|
store/reducer.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const initState = { inpVal: '', list: ['蓝天', '白云', '阳光', '大海'] }
export default (state=initState, action) => { const newState = JSON.parse( JSON.stringify(state));
switch (action.type) { case 'CHANGE_INPUT_VAL': newState.inpVal = action.value; return newState; case 'ADD_TODO_ITEM': newState.list.push(action.value); newState.inpVal = ''; return newState; case 'DELETE_TODO_ITEM': newState.list.splice(action.index, 1); return newState; }
return state; }
|
subscribe 监控 store 中值的变化
todoList.js:
1 2 3 4 5 6 7
| componentDidMount () { store.subscribe(this.handleStoreChange); }
handleStoreChange = () => { this.setState(store.getState()); }
|
通过 redux 管理状态的过程基本实现了,todoList.js 总和起来就是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| import React, { Component } from 'react'; import store from '../store';
class c extends Component {
state = store.getState()
componentDidMount () { store.subscribe(this.handleStoreChange); }
render () { return ( <> <div> <input value={ this.state.inpVal } onChange={ this.handleChange }></input> <button onClick={ this.handleAdd }>添加</button> </div> <ul> { this.state.list.map( (item, index) => ( <li key={item + index}> { item } <button onClick={ ()=>{ this.handleDelete(index) } }>X</button> </li> )) } </ul> </> ) }
handleChange = (e) => { const action = { type: 'CHANGE_INPUT_VAL', value: e.target.value } store.dispatch(action); }
handleAdd = () => { const action = { type: 'ADD_TODO_ITEM', value: this.state.inpVal } store.dispatch( action ); }
handleDelete = (index) => { const action = { type: 'DELETE_TODO_ITEM', index } store.dispatch(action); }
handleStoreChange = () => { this.setState(store.getState()); } }
export default c;
|
但 action 这样放在组件中若是出错了也不太好找,所以把它整合起来放在一个文件夹中, 所以下面几个都是整理代码
action 抽离成一个组件
在 store 中整合 actionTypes,并把需要的 action 导出,在这样在其他组件中直接调用就行了
store/actionTypes.js :
1 2 3 4
| export const CHANGE_INPUT_VAL = 'CHANGE_INPUT_VAL'; export const ADD_TODO_ITEM = 'ADD_TODO_ITEM'; export const DELETE_TODO_ITEM = 'DELETE_TODO_ITEM'; export const COUNT_ADD = 'COUNT_ADD';
|
store/actionCreators.js :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import * as Types from './actionTypes';
export const getTodoChangeInputValAction = (value) => { return { type: Types.CHANGE_INPUT_VAL, value } }
export const getTodoAddItemAction = (value) => { return { type: Types.ADD_TODO_ITEM, value } }
export const getTodoDeleteItemAction = (index) => { return { type: Types.DELETE_TODO_ITEM, index } }
export const getCountAddAction = (n) => { return { type: Types.COUNT_ADD, n } }
|
在 todoList.js 中直接使用 actionCreators:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import React, { Component } from 'react'; import store from '../store'; import * as Actions from '../store/actionCreators';
handleChange = (e) => { const action = Actions.getTodoChangeInputValAction(e.target.value); store.dispatch(action); }
handleAdd = () => { const action = Actions.getTodoAddItemAction( this.state.inpVal ); store.dispatch( action ); }
|
抽离 reducer
现在 这个页面有两个小功能,一个是 todolist,一个是counter ,但他们的 reducer 都是写在一起的,所以这次把它们两个抽离出来,目录结构具体如下:
其中,store/reducer/counter.js :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import * as Types from '../actionTypes';
const initState = { count: 0 }
export default (state=initState, action) => { const newState = JSON.parse( JSON.stringify(state));
switch (action.type) { case Types.COUNT_ADD: newState.count = newState.count + action.n; return newState; }
return state; }
|
store/reducer/todoList.js :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import * as Types from '../actionTypes';
const initState = { inpVal: '', list: ['蓝天', '白云', '阳光', '大海'] }
export default (state=initState, action) => { const newState = JSON.parse( JSON.stringify(state));
switch (action.type) { case Types.CHANGE_INPUT_VAL: newState.inpVal = action.value; return newState;
case Types.ADD_TODO_ITEM: newState.list.push(action.value); newState.inpVal = ''; return newState;
case Types.DELETE_TODO_ITEM: newState.list.splice(action.index, 1); return newState;
}
return state; }
|
store/reducer/index.js , 我们用 redux 中的 combineReducers ,把两个文件中的 actionCreators整合在一起:
1 2 3 4 5 6 7 8
| import { combineReducers } from 'redux'; import todoList from './todoList'; import counter from './counter';
export default combineReducers({ todoList, counter })
|
reducer整合后使用的函数名有没有变化,在 component/counter.js :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import React, { Component } from 'react'; import store from '../store'; import { getCountAddAction } from '../store/actions/counter';
class c extends Component {
state = store.getState().counter
componentDidMount () { store.subscribe(()=>{ this.setState(store.getState().counter) }) }
render () { return ( <div> { this.state.count } <button onClick={ this.handleClick }>add</button> </div> ) }
handleClick = () => { const action = getCountAddAction(3); store.dispatch(action); } }
export default c;
|