blog
blog copied to clipboard
redux 快速入门
- 官网: http://redux.js.org/
- 中文文档: http://cn.redux.js.org/
应用中所有的 state 都以一个对象树的形式储存在一个单一的 store 中。惟一改变 state 的办法是触发 action,一个描述发生什么的对象。为了描述 action 如何改变 state 树,你需要编写 reducers。
三大原则
- 单一数据源:整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。
- State 是只读的: 惟一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
- 使用纯函数来执行修改: 为了描述 action 如何改变 state tree ,你需要编写 reducers
基础
action
Action 是把数据从应用传到 store 的有效载荷。它是 store 数据的唯一来源。一般使用 store.dispatch()
将action 传到store
const action = {
type: 'ADD_TODO',
text: 'Build my first Redux app'
}
store.dispatch(action)
一个action必须有type
字段,描述action做的事情,其他字段都为可选项,是action携带的数据,只作为传递数据用途。
action 创建函数
Action 创建函数 就是生成 action 的方法。可以简单的理解为调用这个函数就会创建action并且自动调用store.dispatch()
。当我们使用react-redux
工具时,可以自动绑定dispatch,所以函数可以简化为这样:
function addTodo(text) {
return {
type: 'ADD_TODO',
text
}
}
// 在组件中调用,自动dispatch
this.props.addTodo('test task')
redux会dispatch函数的返回值
reducer
action 只是描述了有事情发生了这一事实,reducer根据action的描述怎么去更新状态。reducer就是一个纯函数,接收旧的state和action,返回新的state。使用函数默认值设置初始状态。
function todo(state = {todos: []}, action){
switch(action.type){
case 'ADD_TODO':
return {
...state,
todos: [
...state.todos,
{
id: state.todos.length + 1,
text: action.text
}
]
}
default:
return state;
}
}
注意:永远不要在reducer做这些事
-
修改传入参数
-
执行有副作用操作,如api请求和路由跳转
-
调用非纯函数
-
不修改state,直接返回一个新对象state
谨记 reducer 一定要保持纯净。只要传入参数相同,返回计算得到的下一个 state 就一定相同。没有特殊情况、没有副作用,没有 API 请求、没有变量修改,单纯执行计算
拆分 reducer
应用一旦复杂了,reduer会非常长,为了更好的模块化管理,拆分成多个小reducer,然后合并。Redux 提供了 combineReducers() 工具类合并reducer。但只能有一个根reducer,相当于react组件只有一个根标签。
import { combineReducers } from 'redux';
const todoCrud = (state, action) => {}
const todoVisable = (state, action) => {}
const todo = combineReducers({
crud: todoCrud,
visable: todoVisable
})
export default todo;
store
action 来描述“发生了什么”,reducers 来根据 action 更新 state 。Store就是把他们联系到一起的对象。Redux 应用只有一个单一的 store
- 维持应用的 state;
- 提供 getState() 方法获取 state;
- 提供 dispatch(action) 方法更新 state;
- 通过 subscribe(listener) 注册监听器;
- 通过 subscribe(listener) 返回的函数注销监听器。
创建store
redux 提供 createStore()
工具创建store,接收根reducer作为参数
import {createStore} from 'redux';
import rootReducer from './reducers';
const store = createStore(rootReducer)
搭配react
Redux 和 React 之间没有关系。Redux 支持 React、Angular、Ember、jQuery 甚至纯 JavaScript。他只是一个状态管理工具而已。
redux与react搭配使用react-redux
工具。
Provider
将 <Provider />
包围需要使用redux状态的组件。如根组件。provider 需要传递 store 进去。
import { render } from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import App from './containers/App';
import todoApp from './reducers';
let store = createStore(todoApp);
let rootElement = document.getElementById('root')
render(
<Provider store={store}>
<App />
</Provider>,
rootElement
)
connect && bindActionCreators
在Provider包围的范围内,通过 react-redux 提供的 connect()
方法将包装好的组件连接到Redux。传入组件需要的状态与action 生成函数。
bindActionCreators
可以将普通的函数自动绑定变为 action生成函数
import {connect, bindActionCreators} from 'redux';
import {addTodo} from './actions';
class App extends React.Component{
handlerClick(e){
this.props.todos // 来自于redux的 state.todos
this.props.addTodo('this is my task') // 调用 action 生成函数
}
render(){
return <div onClick={this.handlerClick}></div>
}
}
// 将 reudx 的状态传递给组件,可在组件的props获取
function mapStateToProps(state){
return {
todos: state.todos
}
}
// 将普通函数绑定转化为action生成器
function mapDispatchToProps(dispatch){
return bindActionCreators({addTodo}, dispatch);
}
// 连接
export default connect(mapStateToProps, mapDispatchToProps)(App)
我们可以使用 es7 的 decorator 简化代码书写。
@connect(
state => ({todos: state.todos})
disatch => bindActionCreators({todos, dispatch})
)
export default class App extends React.Component{}
中间件
在 action 被发起之后,到达 reducer 之前可以使用中间件处理action。
异步action
redux-thunk
是处理异步action的redux中间件,它的所有代码如下
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
如果action是函数,那就需要显示调用 dispatch 才能真正触发action,使用方法如下
const getTodo(){
return dispatch => {
request.get(url).end((err, res) => {
dispatch({
type: 'GET_TODO',
data: res.body
})
})
}
}