redux基础第一讲

在大型项目中,如果完全使用react管理数据将会是一件很麻烦的事情。这一章,我们将介绍如何使用redux来管理你的应用状态。

Redux的基本原则

我们知道,react的数据都是“单向数据流”,即由父组件往子组件一层一层的传递。redux在此基础上又强调了三个基本原则:

  • 唯一数据源
  • 保持状态只读
  • 数据改变只能通过纯函数完成
    下面,让我们具体分析这三条基本原则:

唯一数据源

整个应用只有一个store,所有组件的数据源就是这个store上的状态。这句话怎么理解呢?比如,我们要做一个博客系统,这个博客系统只有用户信息模块和显示博客列表模块。那么,store上的状态应该像下面这样设计:

initState = {
    userInfo: ...,
    blogList: ...
}

initState.userInfo用来存储用户信息,initState.blogList用来存储博客信息。这样,我们就可以在一个store上存储所有的应用信息。

保持状态只读

不可直接修改状态,要通过派发一个action对象去完成修改。关于action,后面再做讲解。

数据改变只能通过纯函数完成

这里所说的純函数指的是就是reducer。那么何为純函数呢?純函数指的就是返回结果必须完全由参数决定,且不得修改参数值。

reducer只负责计算state,并不负责存储state。存储state的工作交给Redux框架去做。在这里,你只需要知道当我们派发了一个action后,redux会根据action的type值,在reducer找到对应的条件分支,然后去处理这个分支下的行为,state的计算就在这个条件分之下完成。

Redux实例

下面,我们将创建一个简单的计数器应用来讲解具体该如何使用redux。应用界面的渲染必须通过state或props的改变来触发,而state或props的改变是用户操作行为导致的结果。上面说过,要修改状态需要派发一个action,这个action的派发,就是用户行为导致的。

action

action顾名思义代表一个“动作”,不过这个动作是一个普通的javascript对象。action对象必须有一个名为type的字段,代表这个action对象的类型。比如,计数器有加减操作,那么action的type可定义为'increment'或'decrement'。

定义action通常需要两个文件,一个用于保存action的类型,一个用于创建action的构造函数。

在ActionTypes.js文件中,我们定义action的类型。

//  ActionTypes.js
export const INCREMENT = 'increment';
export const DECREMENT = 'decrement';

上面分别定义了用户的两个动作,加和减。

我们在actions.js中定义action的构造函数。注意,创建的是action构造函数,而不是action本身,action构造函数会返回一个action。

// Actions.js
import * as ActionTypes from './ActionTypes.js';

export const increment = (counterCaption) => {
  return {
    type: ActionTypes.INCREMENT,
    counterCaption: counterCaption
  };
};

export const decrement = (counterCaption) => {
  return {
    type: ActionTypes.DECREMENT,
    counterCaption: counterCaption
  };
};

store

action创建了之后,我们需要将它派发出去,以此来改变state。上面已经介绍过,state被存储在store,store提供dispatch()方法来派发action,dispatch接收action对象作为参数。

我们先创建一个store.js文件,输出全局唯一的那个store。

//  Store.js
import {createStore} from 'redux';
import reducer from './Reducer.js';

const initValues = {
  'First': 0,
  'Second': 10,
  'Third': 20
};
const store = createStore(reducer, initValues);

export default store;

redux提供createStore方法来创建store。createStore方法接收两个参数,第一个参数代表更新状态的reducer,第二个参数是状态的初始值。关于createStore的具体用法,后面章节会介绍,这里只要知道这两个参数就行。

reducer

接下来创建reducer函数,reducer用于计算state。

// Reducer.js
import * as ActionTypes from './ActionTypes.js';

export default (state, action) => {
  const {counterCaption} = action;

  switch (action.type) {
    case ActionTypes.INCREMENT:
      return {...state, [counterCaption]: state[counterCaption] + 1};
    case ActionTypes.DECREMENT:
      return {...state, [counterCaption]: state[counterCaption] - 1};
    default:
      return state
  }
}

在reducer函数中,根据action.type的判断条件来执行state的计算。上面的代码,我们使用了扩展运算符(...),不懂的同学自行补脑吧,在es6的课程有讲解。

记住:reducer中绝对不能直接修改state。因为reducer是一个純函数,純函数的返回结果完全由传入的参数决定,且不可修改参数值。 使用扩展运算符,实际上复制了一个state副本,操作的是这个副本,犹如jquery中$.extend()的用法。

view

接下来,我们看一下view部分。view中有三个组件,最外层的是ControlPanel组件。

// ControlPanel.js
class ControlPanel extends Component {
    render() {
        return (
            <div style={style}>
                <Counter caption="First"/>
                <Counter caption="Second"/>
                <Counter caption="Third"/>
                <hr/>
                <Summary/>
            </div>
        );
    }
}

ControlPanel组件中包含Counter和Summary两个组件,这里主要介绍Counter组件的写法,Summary组件和Counter组件写法差不多。

首先初始化this.state

// Counter.js
class Counter extends Component {
  constructor(props) {
    super(props);
    ...
    
    this.state = this.getOwnState();
  }

  getOwnState() {
    return {
      value: store.getState()[this.props.caption]
    };
  }
  
  ...
}

我们把从store上获取状态的逻辑放在getOwnState()方法中,store.getState()可获得当前状态,也就是我们在Store.js中定义的initValues。

为了保证store上的状态和this.state保持同步,需要监听store的变化。

onChange() {
    this.setState(this.getOwnState());
}
componentDidMount() {
    store.subscribe(this.onChange);
}
componentWillUnmount() {
    store.unsubscribe(this.onChange);
}

在componentDidMount中。通过store.subscribe监听store变化,只要store发生变化,就会调用onChange方法;在componentWillUnmount函数中,我们把这个监听注销,和componentDidMount中的动作对应。

改变store中状态的唯一方法是派发一个action。如下:

// Counter.js
class Counter extends Component {
  ...
  
  onIncrement() {
    store.dispatch(Actions.increment(this.props.caption));
  }

  onDecrement() {
    store.dispatch(Actions.decrement(this.props.caption));
  }

  ...

  render() {
    const value = this.state.value;
    const {caption} = this.props;

    return (
      <div>
        <button style={buttonStyle} onClick={this.onIncrement}>+</button>
        <button style={buttonStyle} onClick={this.onDecrement}>-</button>
        <span>{caption} count: {value}</span>
      </div>
    );
  }
}

store.dispatch()方法派发一个action后,redux会根据action的type值,查找在reducer函数中相符的判断条件来执行state的计算。

原文地址:https://www.cnblogs.com/renzhiwei2017/p/9480910.html