Redux浅析

Redux基础速览

Posted by Aaronwn on April 18, 2018

Redux

Redux是什么

React 用来处理数据的方式主要有 props 和 state 两种(另外还有一种不常用的 context)。

其中的 props 必须是从父组件传递到子组件,如果嵌套层级很多,props 必须逐级从保存数据的组件层层传递到使用 props 的组件当中。而 state 在使用的时候,必须通过调用 this.setState() 方法,在改变 state 值的同时,触发 React 组件运行的生命周期,来触发界面的更新。 this.setState() 方法可以传递数据、方法、回调函数。在同一次操作中,连续调用多次 this.setState() 方法也会造成许多难以预料的结果,仅仅通过看代码你很难判断出最后值会变成什么。

而我们使用 React 开发界面的主要场景是在Web应用当中,不同于传统的以内容为主的网页。Web应用涉及到非常多的状态数据的改变,包括用户的交互、服务器通信、界面的动画、样式的改变等等内容。

React 是一个专注于视图层的库,在数据的改变,状态管理方面,它并没有做得很好。因此,当我们的应用复杂到一定程度时,就需要引入一些其他的工具库(Redux)来帮助我们解决状态管理的问题。

状态管理又是什么

Component(state) = View

我们通过一个简单的公式来说明这个问题。在 React 的理念当中,组件其实就是一个方法,我们向组件方法传入数据得出要渲染的视图内容。

用 Redux 管理应用的状态数据

React 应用的开发理念告诉我们,在一个应用当中,如果有两个组件需要使用同一数据,那么我们需要把这一组数据提升到它们共同的父组件当中保存;在实际开发当中,应该尽量控制有状态组件(含有 state 的组件)的数量。

那么我们为什么不把所有的状态数据改变,用一种统一的方式描述;既然要控制有状态组件的数量,那么我们为什么不干脆直接把一个应用的所有状态数据存储在一个统一的地方集中管理?

这也就是 Redux 的理念。 img

Action

Action 就是我们上述的,用统一的形式,描述所有改变应用状态数据的操作的方法。说白了,它其实就是一个带有 type 属性的 Javascript 对象:

{
  type: 'INCREMENT',
  value: 1
}

Reducer

Reducer 则是 Redux 的设计理念当中最核心的方法,它接受当前的状态数据以及触发的 Action 作为参数,根据内部 switch 结构的逻辑判断,返回一个新的状态数据:

(previousState, action) => newState

例如在计数器demo中,可以抽象编写出这样一个 Reducer 方法

function counter(state = 0, action) {
    switch (action.type) {
      case 'INCREMENT':
        return state + action.value
      default:
        return state
    }
}

我们可以看到 counter 函数接受 state 和 action 两个参数,返回值则是经过一个 switch 结构判断的新的 state 数据。这样结构的函数也就是我们在使用 Redux 时编写的 Reducer 方法。

这是 Redux 理念当中最核心的一个部分,它决定了一个应用当中的状态数据在不同的 Action 被触发时具体会如何改变。

Store

Store 则是 Redux 当中我们用来存储状态数据的地方,它提供了3个主要的方法:

  • 用来获取当前状态数据的 getState()

  • 用来触发应用 action 动作的 dispatch(action)

  • 用来订阅响应事件(state改变之后进行的操作)的 subscribe(listener)

而在使用 Redux 时,我们可以通过它提供的 createStore 方法,直接从 reducer 函数生成对应的 store :

const { createStore } = Redux;

const store = createStore(counter);

在 React 应用当中使用 Redux

我们可以直接在 React 项目当中使用 Redux:

const render = () => {
  /* 传入 store.getState() 获取 Redux 当中存储的状态数据
   * 传入 store.dispatch() 方法来执行对应 action 修改状态数据
   */
  ReactDOM.render(<Counter
              number={store.getState()}
              onIncrement={() => store.dispatch({
                          type: 'INCREMENT',
                          value: 1
                        })}
             />,
             document.getElementById('root'));
}
// 调用一次 render 方法进行初次渲染
render()
// 使用 store.subscribe 方法订阅 render 这样每次 store.dispatch 方法触发时就会自动调用 render
store.subscribe(render);

react-redux

当然,每次 Redux 当中的状态数据改变时都强制执行 ReactDOM 的 render 方法并不是最优选择。事实上,社区已经开发出了一个名为 react-redux 的库专门来辅助我们对 React 和 Redux 进行协同使用。

/* Provider 充当为整个 React 应用传入 Redux 当中 store 的容器组件
 * connect 用来为需要使用 store 的组件提供相应的状态数据或 dispatch 方法
 */
const { Provider, connect } = ReactRedux;
/* 我们通过 mapStateToProps 来将 Redux 当中的状态数据映射到 React 相应的 props 当中 */
const mapStateToProps = state => ({
  number: state
});

class Counter extends Component {
  constructor(props) {
    super(props);
  }

  handleClick() {
    // 在这里调用传入组件的 dispatch 方法
    this.props.dispatch({
      type: 'INCREMENT',
      value: 1
    });
  }

  render() {
    return (
      <div>
        <Title />
        <Number number={this.props.number} />
        <Button onClick={() => this.handleClick()} />
      </div>
    )
  }
}
/* 我们需要通过 connect 方法来包装一下 React 的 Counter 组件,使其获取到 Redux 的 store 当中的方法和数据 */
Counter = connect(mapStateToProps)(Counter);

总结

  • store 由 Redux 的 createStore(reducer) 生成
  • state 通过 store.getState() 获取,本质上一般是一个存储着整个应用状态的对象
  • action 本质上是一个包含 type 属性的普通对象,由 Action Creator (函数) 产生
  • 改变 state 必须 dispatch 一个 action
  • reducer 本质上是根据 action.type 来更新 state 并返回 nextState 的函数
  • reducer 必须返回值,否则 nextState 即为 undefined
  • 实际上,state 就是所有 reducer 返回值的汇总(本教程只有一个 reducer,主要是应用场景比较简单)

    Action Creator => action => store.dispatch(action) => reducer(state, action) => 原 state state = nextState