koa+react(三)

react+redux实践

现在开始使用react+redux实现前端的组件化和状态机。
主要是把组件分为了两种:容器组件和展示组件。
容器组件:和 redux 和 router 交互,维护一套状态和触发 action。
展示组件:展示组件是在容器组件的内部,他们不维护状态,所有数据通过 props 传给他们,所有操作也是通过回调完成。

注: 如果开发过程中有报eslint错误的话,可自行google,对eslint配置进行完善。

开始

先清空原先的app文件夹里的文件,添加main.js。内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import Todo from './todo';
import createStore from './store';
const store = createStore();
render(
// 这里我们从 react-redux 中获取了一个 Provider 组件,我们把它渲染到应用的最外层。他需要一个属性 store ,他把这个 store 放在context里,给App(connect)用。
<Provider store={store}>
<Todo />
</Provider>,
document.getElementById('example')
);

配置store

app下新建store.js文件,内容为:(上文传到Provider里面的store就是这里创建的状态机,该状态将渗透到后面的raect容器组件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';
import rootReducer from './reducers/main';
const loggerMiddleware = createLogger();
export default function Store(initState) {
const ENV = process.env.NODE_ENV || 'development';
if (ENV === 'development') {
return createStore(rootReducer, initState, applyMiddleware(
thunkMiddleware, // 允许我们 dispatch() 函数
loggerMiddleware // 一个很便捷的 middleware,用来打印 action 日志
), window.devToolsExtension ? window.devToolsExtension() : undefined);
}
return createStore(rootReducer, initState, undefined);
}

reducers

app下新建reducers文件夹,新建todo.js文件,内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module.exports = (state = [], action) => {
switch (action.type) {
case 'ADD_TODO':
return state.slice(0).concat({
text: action.text,
completed: false
});
case 'COMPLETE_TODO':
return [
...state.slice(0, action.index),
Object.assign({}, state[action.index], {
completed: true
}),
...state.slice(action.index + 1)
];
default:
return state;
}
};

新建index.js,内容为:

1
2
3
4
5
6
7
8
9
import { combineReducers } from 'redux';
import todo from './todo';
// 通过combineReducers将多个reducer合并成一个rootReducer:
const rootReducer = combineReducers({
todo
});
export default rootReducer;

actioncreator

app下新建actions文件夹,新建todo.js文件, 内容为:

1
2
3
4
5
6
7
8
9
10
11
12
export function addTodo(text) {
return {
type: 'ADD_TODO',
text
};
}
export function completeTodo(index) {
return {
type: 'COMPLETE_TODO',
index
};
}

容器组件containers

app下添加containers文件夹,新建todo.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
import React, {Component} from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as TodoActions from '../actions/todo';
import AddTodo from '../components/todo/addtodo';
import TodoList from '../components/todo/todolist';
class Todo extends Component {
render() {
const { actions, todo } = this.props;
return (
<div>
<AddTodo
onAddClick={text =>
actions.addTodo(text)
}
/>
<TodoList
todos={todo}
onTodoClick={index => actions.completeTodo(index)}
/>
</div>
);
}
}
Todo.propTypes = {
actions: React.PropTypes.object.isRequired,
todo: React.PropTypes.object.isRequired
};
// 声明 connect 连接
// mapStateToProps是一个函数,返回值表示的是需要merge进props的state
function mapStateToProps(state) {
return {
todo: state.todo
};
}
// mapDispatchToProps负责返回一个 dispatchProps, dispatchProps 是actionCreator的key和
// dispatch(action)的组合。dispatchProps 看起来长这样:{add_todo: (text) => dispatch(action)}
// 但如果我有很多个Action,总不能手动一个一个加。Redux提供了一个方法叫 bindActionCreator。
// bindActionCreators 的作用就是将 Actions 和 dispatch 组合起来生成 mapDispatchToProps 需要生成的内容。
// 它看起来像这样:
// actions = { addTodo: (text) => {type: types.ADD_TODO,text}, completeTodo: (index) => {type: types.COMPLETE_TODO,index}}
function mapDispatchToProps(dispatch) {
const boundTodo = bindActionCreators(TodoActions, dispatch);
return {
actions: Object.assign({}, boundTodo)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Todo);

展示组件components

鉴于代码比较简单,就不粘贴了,具体的可以查看github源码

至此,附上项目目录:

.
├── app
│ ├── actions
│ ├── components
│ ├── constants
│ ├── containers
│ ├── main.js
│ ├── reducers
│ └── store.js
├── build
│ └── bundle.js
├── package.json
├── server
│ ├── babel.js
│ └── server.js
├── views
│ └── index.html
└── webpack.config.js

如果您觉得受益了,欢迎打赏鼓励。