redux 快速入门

@eyasliu 2016-06-20 09:08:59发表于 eyasliu/blog react前端

应用中所有的 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
      })
    })
  }
}