橙子

redux 总结

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'; //引入 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'; //引入 store

<input value={ this.state.inpVal } onChange={ this.handleChange }></input>

//点击事件中 通过 dispatch 去改变 store 的值
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__()
);
// window 这句是为了控制台中能打开 redux

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); // 订阅了 store 中的值
}

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;