2019-10-16 | UNLOCK | 更新时间:2019-10-16 11:3

Redux在开发中该如何使用

在开始前,我们需要知道,在本文中将会是简单的应用Redux,一些复杂和高级阶段的API 可能不会出现,这可能需要自主学习

开始和安装

在项目中,当我们需要Redux时,我们需要使用 npm 安装 Redux 和 React绑定库,开发调试工具

npm install redux --save
npm install react-redux --save
npm install redux-devtools --save-dev

核心和概念

Action

把数据从应用传到stort的有效载体,它是store数据的唯一来源.Action 本质上是对象。我们约定,action 内必须使用一个字符串类型的 type 字段来表示将要执行的动作。同时建议使用单独的模块或文件来存放 action

const ADD_TODO = 'ADD_TODO'
{
  type: ADD_TODO,
  text: 'Build my first Redux app'
}

由于 action 的数量众多,因此需要构建函数方法。 Action 创建函数 就是生成 action 的方法,只是简单的返回一个 action

function addTodo(text) {
  return {
    type: ADD_TODO,
    text
  }
}

// Redux 中只需把 action 创建函数的结果传给 dispatch() 方法即可发起一次 dispatch 过程
// dispatch(addTodo(text))

Reducer

Reducers 指定了应用状态的变化如何响应 actions 并发送到 store 的,记住 actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state 当我们确定了 state 对象的结构,就可以开始开发 reducer。reducer 就是一个纯函数,接收旧的 state 和 action,返回新的 state

function todoApp(state = initialState, action) {
  // 这里暂不处理任何 action,
  // 仅返回传入的 state。
  // initialState 可能就是state数据中的一个
  return state
}

永远不要在 reducer 里做这些操作

  • 修改传入参数;
  • 执行有副作用的操作,如 API 请求和路由跳转;
  • 调用非纯函数,如 Date.now() 或 Math.random()。

Store

Store是存储数据的地方,它就像是一个容器。用来维持应用所有的 state 树 的一个对象,整个应用只有一个 Store。redux 提供 createStore(fn) 并接收一个函数来生成一个 Store,

import { createStore } from 'redux'
import todoApp from './reducers'  //由下文中的Reducer集合文件
let store = createStore(todoApp) 

//createStore() 的第二个参数是可选的, 用于设置 state 初始状态
let store = createStore(todoApp, window.STATE_FROM_SERVER)

Store的职责

  • 提供 getState() 方法获取 state;
  • 提供 dispatch(action) 方法更新 state;
  • 通过 subscribe(listener) 注册监听器;
  • 通过 subscribe(listener) 返回的函数注销监听器。

store.dispatch()

dispactch接受一个action对象作为参数并发送出去。你可以在任何地方调用 store.dispatch(action);Store 会把两个参数传入 reducer: 当前的 state 树和 action 。Action 是把数据传入 store 的惟一途径,所以任何数据,无论来自 UI 事件,网络回调或者是其它资源如 WebSockets,最终都应该以 action 的形式被 dispatch

Redux 的基本原则

  • 唯一的数据源

整个应用的 state 被储存在一棵 object tree 中,且这个 object tree 只存在唯一的 store

这让同构应用开发变得非常容易。来自服务端的 state 可以在无需编写更多代码的情况下被序列化并注入到客户端中。由于是单一的 state tree ,调试也变得非常容易。在开发中,你可以把应用的 state 保存在本地,从而加快开发速度。此外,受益于单一的 state tree ,以前难以实现的如“撤销/重做”这类功能也变得轻而易举

  • State是只读的

唯一改变 state 的方法就是触发 action,通过 Reducer 对 state 进行修改

这样确保了视图和网络请求都不能直接修改 state,相反它们只能表达想要修改的意图。因为所有的修改都被集中化处理,且严格按照一个接一个的顺序执行,因此不用担心 race condition 的出现。 Action 就是普通对象而已,因此它们可以被日志打印、序列化、储存、后期调试或测试时回放出来

  • 使用纯函数进行修改

Reducer 只是一些纯函数,它接收先前的 state 和 action,并返回新的 state。刚开始你可以只有一个 reducer,随着应用变大,你可以把它拆成多个小的 reducers,分别独立地操作 state tree 的不同部分,因为 reducer 只是函数,你可以控制它们被调用的顺序,传入附加数据,甚至编写可复用的 reducer 来处理一些通用任务,如分页器

项目结构参考

src/
  actions/     【存储action创建函数的文件夹】
    index.js
  components/  【存储展示组件的文件夹】
    App.js
    ...
  containers/  【存储容器组件的文件夹】
    Add.js
    ...
  reducers/    【存储reducer的的文件夹】
    index.js
    add.js
    ...

展示组件和容器组件的区分

展示组件 容器组件
作用 描述如何展示(骨架,样式) 描述如何运行(数据获取,状态更新)
直接使用redux
数据来源 props 监听Redux state
数据修改 从Props调用回调函数 向Redux 派发action
入口文件 index.js
... import { Provider } from 'react-redux' import { createStore } from 'redux' import rootReducer from './reducers' import App from './components/App' const store = createStore(rootReducer) // Provider是React Redux 的高阶组件,用于将 Redux 绑定到 React render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') ) ...
容器组件 TodoList.js
import { connect } from 'react-redux' import { toggleTodo } from '../actions' import List from '../components/TodoList' // const getVisibleTodos = (todos, filter) => { switch (filter) { case 'SHOW_COMPLETED': return todos.filter(t => t.completed) //数组通过completed属性筛选 case 'SHOW_ACTIVE': return todos.filter(t => !t.completed) case 'SHOW_ALL': default: return todos } } const mapStateToProps = function(state){ console.log(state) return ({ todos: getVisibleTodos(state.todos, state.visibilityFilter) }) } const mapDispatchToProps = function(dispatch){ console.log('1') return({ toggleTodo: id => dispatch(toggleTodo(id)) }) } export default connect( //以注入对象的方式,以健取值 mapStateToProps, //注入todos mapDispatchToProps )(List)