Redux源码分析(1) - Redux介绍及使用

一、Redux生态的介绍

  关于 Redux 的介绍能够参考: Redux 中文文档。Redux 是 JavaScript 状态容器,提供可预测化的状态管理。一般在 React 中使用 Redux 时,还使用到React-Redux、Redux-Saga。javascript

  Redux做为全局的状态管理器,整个应用的 state 被储存在一棵 object tree 中,而且这个 object tree 只存在于惟一一个 store 中。惟一改变 state 的方法就是触发 action,action 会经过执行 reducer 来改变 state 的值, state 的改变会引发视图层的改变,即 Component 的改变。Redux 的工做流以下图所示:java

  在实际开发过程当中,除 action 和 reducer 以外,还会用到一些中间件 middleware ,中间件本质就是在 dispatch 更改 reducer 以前作一些操做,这里涉及到 Redux 的一个重要 api:applyMiddleware ,在后续篇幅中会详细介绍。在实际的业务开发中,将全部更新 state 的逻辑都放入到单个 reducer 函数中都将会让程序变得不可维护,所以对 reducer 的拆分是颇有必要的,这里也涉及到 Redux 的另一个重要 api:combineReducers ,关于这部分在后文也会详细分析。若是加上这部分分析, Redux 的工做流就能够用下图更加详细的说明。react

   Redux 做为全局的状态管理器,自己与 React 没有直接关系的。若是让 Redux 的状态可以在 React 的视图组件中展现,而且在视图组件能够更改 Redux 的状态,这就依赖于 React-Redux 。React-Redux 的核心是 Provider 组件 和 connect 方法 。git

   Provider 的本质就是 React 的 context属性 ,经过在将 Provider 组件包裹 组件,并传入 Redux 的 store 属性,这样在 的后代组件中就能够经过获取到 store 中 state 的值。github

<Provider store={store}>
    <App /> </Provider>,
复制代码

   connect 方法的做用从字面上能够理解为:将 Redux 中 store 和 视图 View 关联起来。connect 本质是一个高阶函数。其中mapStateToProps能够解释为:将 store 中的 state 的映射到 Component 中,完成 Model 层到 View 层的展现。mapDispatchToProps 能够解释为:如何从 Component 中去更改 store 中的 state,完成从 View 层到 Model 层的改变。redux

connect(mapStateToProps, mapDispatchToProps)(MyComponent)
复制代码

   固然在 Redux 的 store 中的 state 发生变化,也会完成 Redux 的订阅 subscribe 的执行,也包括 mapStateToProps、 mapDispatchToProps 等方法的从新执行 ,关于 react-redux 可后续篇幅专门分析。api

   Redux-Saga 自己就是 Redux 的一个中间件 middleware ,是为了解决 Redux 的一些异步的处理, 是经过 ES6 的 Generator 去实现的,让异步流程能够经过同步的方式去实现。数组

二、如何使用Redux

   了解了 Redux 的一些生态介绍以后,咱们有必要看看 Redux 是如何使用,只有在熟练使用的基础上,咱们才能更好的对源码有清晰的认识。下边截取了一些重要的代码,完整代码克制 克隆app

2.1 项目入口引用

   在项目的入口文件处,分别引用了 react-Redux 中的 Provider 组件;redux 中的 createStore 方法和 applyMiddleware 方法,项目的 reducer 定义在 reducer.js 页面中。为了后续源码解读中可以更好的阐述中间件部分,此处定义了两个简单的中间件 Logger 和 Test。中间件本质是一个高阶函数,分别接受 store 、next、 action 参数,经过调用 applyMiddleware方法将中间件组合起来使用,这里先给出结论,后续中间件的分析中会详细说明:中间件的组合使用本质上就是改变 next 的值,其中 store 对应为 Redux 的数据模型 store, action 本质上是 JavaScript 普通对象。咱们约定,action 内必须使用一个字符串类型的 type 字段来表示将要执行的动做。dom

//index.js

import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from './redux';
import rootReducer from './reducers';
import App from './components/App';

// middleware
const Logger = (store) => (next) => (action) => {
    console.info('logger start');
    let result = next(action);
    console.info('logger end');
};
const Test = (store) => (next) => (action) => {
    console.info('test start');
    let result = next(action);
    console.info('test end');
};

let store = createStore(rootReducer, applyMiddleware(Logger, Test));

render(
    <Provider store={store}> <App /> </Provider>,
    document.getElementById('root')
);
复制代码

   createStore 方法的官方介绍以下,简单来说:reducer 既能够是单个 reducer 对象 也能够是经过 combineReducers 组合的 reducer 对象。preloadedState 若是存在则表示 reducer 的初始值,若是 reducer 参数是经过 combineReducers 组合生成的,则 preloadedState 必须是一个对象,key 值必须与 combineReducers时的key值对应 (否则 redux 怎么知道初始化那个 reducer )。enhancer 的官方解释比较难懂,能够简单理解为 enhancer 就是经过传入中间件到 applyMiddleware 方法产生的结果。

   createStore(reducer, [preloadedState], enhancer) 建立一个 Redux store 来以存放应用中全部的 state。 应用中应有且仅有一个 store。

参数

  • reducer (Function): 接收两个参数,分别是当前的 state 树和要处理的 action,返回新的 state 树。

  • [preloadedState] (any): 初始时的 state。 在同构应用中,你能够决定是否把服务端传来的 state 水合(hydrate)后传给它,或者从以前保存的用户会话中恢复一个传给它。若是你使用 combineReducers 建立 reducer,它必须是一个普通对象,与传入的 keys 保持一样的结构。不然,你能够自由传入任何 reducer 可理解的内容。

  • enhancer (Function): Store enhancer 是一个组合 store creator 的高阶函数,返回一个新的强化过的 store creator。这与 middleware 类似,它也容许你经过复合函数改变 store 接口。

2.2 reducer的定义

   reducer 接收两个参数,分别是当前的 state 树和要处理的 action,返回新的 state 树。以 实例中 todos 为例,state 默认为返回空数组 [] , 根据 action.type 的不一样类型返回不一样的值。在当期实例中,reducer 是经过 combineReducers 组合生成的, 在实际开发中全部 state 和 改变 state 的逻辑放在一个 reducer 中显然是不合适,combineReducers 的意义就在对各个业务模块的 reducer 进行组合,最后传递给 redux 的 createStore 方法,继而生成一个的状态数。

   合并后的 reducer 能够调用各个子 reducer,并把它们返回的结果合并成一个 state 对象。 由 combineReducers() 返回的 state 对象,会将传入的每一个 reducer 返回的 state 按其传递给 combineReducers() 时对应的 key 进行命名。

   官方文档的解释能够简单理解为:combineReducers 会将各个 reducer 按照 key 值组合(当前实例是经过 ES6 语法简写),在你 dispatch(action) 时,也会根据你 key 值来改变 store 中 对应的 state 的值。

// rootReduce.js

import { combineReducers } from '../redux'
import todos from './todos'
import visibilityFilter from './visibilityFilter'

export default combineReducers({
  todos,
  visibilityFilter
})


//todos.js

const todos = (state = [], action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        {
          id: action.id,
          text: action.text,
          completed: false
        }
      ]
    case 'TOGGLE_TODO':
      return state.map(
        todo =>
          todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
      )
    default:
      return state
  }
}
复制代码

2.3 组件内使用

   此示例中经过 react-redux 将 redux 和 react 关联起来,根据上文的介绍 react-redux 的 connect 方法经过 :

  • mapStateToProps 实现数据从 store 传递到 component,因为当前实例中 reducer 是经过 combineReducers 组合而成,所以在引用时就须要经过对应的 key 去引用 :state.todos, state.visibilityFilter ( 相似于命名空间的概念)。
  • mapDispatchToProps 可让 component 可以改变 store 的状态。当 mapDispatchToProps 返回是函数时,默认的参数是 store.dispatch。
// Component

const TodoList = ({ todos, toggleTodo }) => (
  <ul> {todos.map(todo => ( <Todo key={todo.id} {...todo} onClick={() => toggleTodo(todo.id)} /> ))} </ul> ) const mapStateToProps = state => ({ todos: getVisibleTodos(state.todos, state.visibilityFilter) }) const mapDispatchToProps = dispatch => ({ toggleTodo: id => dispatch(toggleTodo(id)) }) export default connect( mapStateToProps, mapDispatchToProps )(TodoList) 复制代码

2.4 小结

  以上demo实例基本上涵盖了redux 的基本使用,并对于 react-redux 也有一些简单的介绍,完整的理解上述demo的流程,对于后续详细源码分析大有益处。

相关文章
相关标签/搜索