实践四
延续Todo List示例,使用redux & immutabel-js对项目进行改造。
遵循原则
单一数据源(整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中)
State 是只读的(惟一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象)
使用纯函数来执行修改(为了描述 action 如何改变 state tree ,需要编写 reducers)
Dumb组件存入于components目录中,用函数式组件来实现;Smart组件存放于containers目录中。
constants目录存放所有action type常量定义,actions目录存放所有action creators
代码分析
我第一步在没有使用immutable-js的情况下完成了引入redux的改造,然后再加入了immutable-js,逐步体验了各个库引入带来的变化与好处。
引入redux
新增constants/ActionTypes.js
export const ADD_TODO = 'ADD_TODO'
新增actions/index.js
import * as types from '../constants/ActionTypes'
export const addTodo = text => ({ type: types.ADD_TODO, text })
新增reducers/index.js, 统一加载reducer处理入口,方便分文件编写相同模块的处理函数。
import { combineReducers } from 'redux'
import todos from './todos'
const rootReducer = combineReducers({
todos
})
export default rootReducer
新增reducer/todos.js, todos模块的处理函数。
import { ADD_TODO } from '../constants/ActionTypes'
import * as __ from 'lodash';
const initialState =
{
items:[]
};
export default function todos(state = initialState, action) {
switch (action.type) {
case ADD_TODO:
let newState = __.cloneDeep(state); //深拷贝,不能直接改原state
newState.items.push({key:new Date().getTime(),text:action.text});
return newState;
default:
return state
}
}
修改App.js
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import * as TodoActions from '../actions'; //集中放置,方便统一导入
class App extends Component {
static propTypes = { //类静态变量的方式
todos: PropTypes.array.isRequired,
actions: PropTypes.object.isRequired
};
componentDidMount (){ //具有生命周期的组件不能函数组件实现
this.InputComponent.focus();
};
render() {
var {todos,actions} = this.props; //解构属性
return (
<div>
<InputAndButton ref={comp => { this.InputComponent = comp; }} onSave={actions.addTodo}/>
<LiList items={todos}/>
</div>
);
};
}
const mapStateToProps = state => ({
todos: state.todos.items //注意,这块是state.todos.items,中间的todos是因底层封装被带入,不能少,多用工具观察state结构,能明白不少问题。
})
const mapDispatchToProps = dispatch => ({
actions: bindActionCreators(TodoActions, dispatch) //统一引入,这块能很简洁
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(App)
修改index.js
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import reducer from './reducers'
const store = createStore(reducer) //初始化state
render(
<Provider store={store}> //加载应用
<App />
</Provider>,
document.getElementById('root')
);
引入immutable-js
为避免对象的深拷贝,使用Immutable,它提供了简洁高效的判断数据是否变化的方法,可以极大提高性能。我使用redux-immutable完成改造。
修改src/reducers/index.js
-import { combineReducers } from 'redux'
+import { combineReducers } from 'redux-immutable' //切换组合函数
import todos from './todos'
const rootReducer = combineReducers({ //其余地方无需改变
...
修改src/reducers/todos.js
import { ADD_TODO } from '../constants/ActionTypes'
-import * as __ from 'lodash'; //不再需要辅助函数
+import {Map, List} from 'immutable'; //引入immutable的相应数据结构
-const initialState =
- {
- items:[]
- };
+let initialState = Map({ //使用immutable-js数据结构
+ items: List([])
+ });
export default function todos(state = initialState, action) {
switch (action.type) {
case ADD_TODO:
- let newState = __.cloneDeep(state);
- newState.items.push({key:new Date().getTime(),text:action.text});
- return newState;
+ return state.update('items', value => value.push({key:new Date().getTime(),text:action.text})); //操作更加简洁
default:
return state
}
修改src/containers/App.js
const mapStateToProps = state => ({
- todos: state.todos.items
+ todos: state.get('todos').get('items') //使用immutable-js的Map操作接口
})
项目地址
https://git.oschina.net/zhoutk/reactodo.git
https://github.com/zhoutk/reactodo.git
使用方法
git clone https://git.oschina.net/zhoutk/reactodo.git
or git clone https://github.com/zhoutk/reactodo.git
cd reactodo
npm i
git checkout todo_list_react
npm start
小结
这次实践,理解了redux的原理,学会了如何利用redux来改造我们的项目;并且利用immutable-js优化了state的操作及性能。