在React中使用Redux

Posted 每天都要进步一点点

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在React中使用Redux相关的知识,希望对你有一定的参考价值。

1.创建项目

create-react-app test-redux
cd test-redux
yarn start
yarn eject // 显示配置详细文件

2.在React的思想中,视图的变化对应的是数据的变化,data -> ui,这里的data可以是组件的新state,或者接收到的新props和context。由于Redux的数据与React组件状态互相独立,这里就需要一个桥梁来连接React与Redux,也就是react-redux。

react-redux这个库提供的api有:ProviderconnectconnectAdvanced

Provider

Provider是一个React组件,这个组件实现很简单,一共55行代码,它只是接收store、children这两个属性,并且定义了包含store和storeSubscription字段的childContext:

// https://github.com/reactjs/react-redux/blob/master/src/components/Provider.js
export default class Provider extends Component {
  getChildContext() {
    return { store: this.store, storeSubscription: null }
  }

  constructor(props, context) {
    super(props, context)
    this.store = props.store
  }

  render() {
    return Children.only(this.props.children)
  }
}

...

Provider.propTypes = {
  store: storeShape.isRequired,
  children: PropTypes.element.isRequired
}
Provider.childContextTypes = {
  store: storeShape.isRequired,
  storeSubscription: subscriptionShape
}

Provider的render方法简单的返回了props.childrenProvider的主要作用是提供在组件树的某一个节点通过context获取store的能力

一般在项目中,会将Provider这个组件放在整个应用的最顶层,然后将使用Redux.createStore方法创建的store对象传给Provider:

const store = createStore(rootReducer);

ReactDOM.render(<Provider store={store}><App/></Provider>, document.getElementById(‘app‘));

// or

ReactDOM.render((
  <Provider store={store}>
    <Router history={hashHistory}>
      <Route path="/">
        ...
      </Route>
    </Router>
  </Provider>
), document.getElementById(‘app‘));

connect

在顶层加入Provider组件之后,我们就可以在组件树的节点中通过context获取到store对象了,比如我们将计数器使用React组件重写:

/**
 * Counter 组件
 */
import React, { Component } from ‘react‘;
/**
 * 引入 prop-types
 * yarn add prop-types
 */
import PropTypes from ‘prop-types‘;

class Counter extends React.Component {
  render() {
    // 获取 store
    const { store } = this.context;
    // 使用store.getState方法获取保存在store中数据
    const count = store.getState();

    return (
      <div>
        <p>Count: {count}</p>
        {/*通过 store.dispatch 触发 action */}
        <button onClick={() => store.dispatch({type: ‘INCREASE‘})}>+1</button>
        <button onClick={() => store.dispatch(asyncIncrease())}>+1 async</button>
        <button onClick={() => store.dispatch({type: ‘DECREASE‘})}>-1</button>
      </div>
    );
  }
}

/**
 * 通过给组件定义一个包含store的contextTypes
 * 我们就可以在组件内部使用this.context.store获取到store对象
 */
Counter.contextTypes = {
  store: PropTypes.object.isRequired
}

export default Counter;

在上面的Counter组件中,通过给组件定义一个包含store的contextTypes,我们就可以在组件内部使用this.context.store获取到store对象,然后使用store.getState方法获取保存在store中数据并在渲染。

看起来很不错,我们可以将store的数据通过React组件渲染到UI上了,并且同样可以点击按钮来触发action。但是点击按钮你就会发现,显示的Count值一直是0,我们使用store.subscribe方法来观察store状态的变化:

/**
 * Counter 组件
 */
import React, { Component } from ‘react‘;
/**
 * 引入 prop-types
 * yarn add prop-types
 */
import PropTypes from ‘prop-types‘;

class Counter extends React.Component {
  // 生命周期--组件即将加载完毕
  componentWillMount(){
    // 使用store.subscribe方法来观察store状态的变化
    this.context.store.subscribe(() => {
      console.log(`new store state: ${store.getState()}`);
    });
  }
  ...
}

/**
 * 通过给组件定义一个包含store的contextTypes
 * 我们就可以在组件内部使用this.context.store获取到store对象
 */
Counter.contextTypes = {
  store: PropTypes.object.isRequired
}

export default Counter;

打开控制台,按下按钮时,可以发现store里的状态是有变化的:

问题的原因其实在文章的开篇已经提到过了,只有当组件的state、props、context变化时才会引起UI的重新渲染,重新执行render方法。而这里的this.context.store只是一个包含了getState、dispatch等方法的一个对象,store中状态的更新并不会修改store这个对象,变的只是store.getState()的结果而已,所以组件Counter组件并不会触发重新渲染。

一个解决办法是,在store.subscribe中使用this.forceUpdate方法来强制组件重新渲染:

/**
 * Counter 组件
 */
import React, { Component } from ‘react‘;
/**
 * 引入 prop-types
 * yarn add prop-types
 */
import PropTypes from ‘prop-types‘;

class Counter extends React.Component {
  // 生命周期--组件即将加载完毕
  componentWillMount(){
    // 使用store.subscribe方法来观察store状态的变化
    this.context.store.subscribe(() => {
      console.log(`new store state: ${store.getState()}`);
      // 在store.subscribe中使用this.forceUpdate方法来强制组件重新渲染
      this.forceUpdate();
    });
  }
  ...
}

/**
 * 通过给组件定义一个包含store的contextTypes
 * 我们就可以在组件内部使用this.context.store获取到store对象
 */
Counter.contextTypes = {
  store: PropTypes.object.isRequired
}

export default Counter;

这下结果是对了

以上是关于在React中使用Redux的主要内容,如果未能解决你的问题,请参考以下文章

无法解析容器中的存储(React,Redux)

React-Redux的使用

将状态传递给 React/Redux 中的递归嵌套组件

Redux&React路由器:梳理调度和导航(history.push)

使用通过 react、redux 和 react-redux 完成的组件以及在 react 应用程序中使用 webpack 构建时出错

redux超易学三篇之二(开始使用react-redux)