React-redux: React.js 和 Redux 架构的结合

更新日期: 2020-03-13阅读: 1.4k标签: redux

通过Redux 架构理解我们了解到 Redux 架构的 store、action、reducers 这些基本概念和工作流程。我们也知道了 Redux 这种架构模式可以和其他的前端库组合使用,而 react-redux 正是把 Redux 这种架构模式和 React.js 结合起来的一个库。


Context

在 React 应用中,数据是通过 props 属性自上而下进行传递的。如果我们应用中的有很多组件需要共用同一个数据状态,可以通过状态提升的思路,将共同状态提升到它们的公共父组件上面。但是我们知道这样做是非常繁琐的,而且代码也是难以维护的。这时会考虑使用 Context,Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。也就是说在一个组件如果设置了 context,那么它的子组件都可以直接访问到里面的内容,而不用通过中间组件逐级传递,就像一个全局变量一样。

在 App -> Toolbar -> ThemedButton 使用 props 属性传递 theme,Toolbar 作为中间组件将 theme 从 App 组件 传递给 ThemedButton 组件。

class App extends React.Component {
  render() {
    return <Toolbar theme="dark" />;
  }
}

function Toolbar(props) {
  // Toolbar 组件接受一个额外的“theme”属性,然后传递给 ThemedButton 组件。
  // 如果应用中每一个单独的按钮都需要知道 theme 的值,这会是件很麻烦的事,
  // 因为必须将这个值层层传递所有组件。
  return (
    <div>
      <ThemedButton theme={props.theme} />
    </div>
  );
}

class ThemedButton extends React.Component {
  render() {
    return <Button theme={this.props.theme} />;
  }
}

使用 context,就可以避免通过中间元素传递 props 了

// Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。
// 为当前的 theme 创建一个 context(“light”为默认值)。
const ThemeContext = React.createContext('light');

class App extends React.Component {
  render() {
    // 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
    // 无论多深,任何组件都能读取这个值。
    // 在这个例子中,我们将 “dark” 作为当前的值传递下去。
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

class ThemedButton extends React.Component {
  // 指定 contextType 读取当前的 theme context。
  // React 会往上找到最近的 theme Provider,然后使用它的值。
  // 在这个例子中,当前的 theme 值为 “dark”。
  static contextType = ThemeContext;
  render() {
    return <Button theme={this.context} />;
  }
}

虽然解决了状态传递的问题却引入了 2 个新的问题。

1. 我们引入的 context 就像全局变量一样,里面的数据可以被子组件随意更改,可能会导致程序不可预测的运行。

2. context 极大地增强了组件之间的耦合性,使得组件的复用性变差,比如 ThemedButton 组件因为依赖了 context 的数据导致复用性变差。

我们知道,redux 不正是提供了管理共享状态的能力嘛,我们只要通过 redux 来管理 context 就可以啦,第一个问题就可以解决了。


Provider 组件

React-Redux 提供 Provider  组件,利用了 react 的 context 特性,将 store 放在了 context 里面,使得该组件下面的所有组件都能直接访问到 store。大致实现如下:

class Provider extends Component {
  // getChildContext 这个方法就是设置 context 的过程,它返回的对象就是 context,所有的子组件都可以访问到这个对象
  getChildContext() {
    return {
      store: this.props.store
    };
  }
  render() {
    return this.props.children;
  }
}

Provider.childContextTypes = {
  store: React.PropTypes.object
}

那么我们可以这么使用,将 Provider 组件作为根组件将我们的应用包裹起来,那么整个应用的组件都可以访问到里面的数据了

import React, { Component } from 'react';
import Reactdom from 'react-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import todoApp from './reducers';
import App from './components/App';

const store = createStore(todoApp);

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)


展示(Dumb Components)组件和容器(Smart Components)组件

还记得我们的第二个问题吗?组件因为 context 的侵入而变得不可复用。React-Redux 为了解决这个问题,将所有组件分成两大类:展示组件和容器组件。

展示组件

展示组件有几个特征

1. 组件只负责 UI 的展示,没有任何业务逻辑

2. 组件没有状态,即不使用 this.state

3. 组件的数据只由 props 决定

4. 组件不使用任何 Redux 的 api

展示组件就和纯函数一样,返回结果只依赖于它的参数,并且在执行过程里面没有副作用,让人觉得非常的靠谱,可以放心的使用。

import React, { Component } from 'react';
import PropTypes from 'prop-types';

class Title extends Component {
  static propTypes = {
    title: PropTypes.string
  }

  render () {
    return (
      <h1>{ this.props.title }</h1>
    )
  }
}

像这个 Title 组件就是一个展示组件,组件的结果完全由外部传入的 title 属性决定。

容器组件

容器组件的特征则相反

1. 组件负责管理数据和业务逻辑,不负责 UI 展示

2. 组件带有内部状态

3. 组件的数据从 Redux state 获取

4. 使用 Redux 的 API

你可以直接使用 store.subscribe() 来手写容器组件,但是不建议这么做,因为这样无法使用 React-redux 带来的性能优化。

React-redux 规定,所有的展示组件都由用户提供,容器组件则是由 React-Redux 的 connect() 自动生成。


高阶组件 Connect

React-redux 提供 connect  方法,可以将我们定义的展示组件生成容器组件。connect 函数接受一个展示组件参数,最后会返回另一个容器组件回来。所以 connect 其实是一个高阶组件(高阶组件就是一个函数,传给它一个组件,它返回一个新的组件)。

import { connect } from 'react-redux';
import Header from '../components/Header';

export default connect()(Header);

上面代码中,Header 就是一个展示组件,经过 connect 处理后变成了容器组件,最后把它导出成模块。这个容器组件没有定义任何的业务逻辑,所有不能做任何事情。我们可以通过 mapStateToProps  和  mapDispatchToProps 来定义我们的业务逻辑。

import { connect } from 'react-redux';
import Title from '../components/Title';

const mapStateToProps = (state) => {
  return {
    title: state.title
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onChangeColor: (color) => {
      dispatch({ type: 'CHANGE_COLOR', color });
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(Title);

mapStateToProps 告诉 connect 我们要取 state 里的 title 数据,最终 title 数据会以 props 的方式传入 Title 这个展示组件。

mapStateToProps 还 会订阅 Store,每当 state 更新的时候,就会自动执行,重新计算展示组件的参数,从而触发展示组件的重新渲染。

mapDispatchToProps 告诉 connect 我们需要 dispatch action,最终 onChangeColor 会以 props 回调函数的方式传入 Title 这个展示组件。

Connect 组件大概的实现如下

export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {
  class Connect extends Component {
    static contextTypes = {
      store: PropTypes.object
    }

    constructor () {
      super()
      this.state = {
        allProps: {}
      }
    }

    componentWillMount () {
      const { store } = this.context
      this._updateProps()
      store.subscribe(() => this._updateProps())
    }

    _updateProps () {
      const { store } = this.context
      let stateProps = mapStateToProps
        ? mapStateToProps(store.getState(), this.props) // 将 Store 的 state 和容器组件的 state 传入 mapStateToProps
        : {} // 判断 mapStateToProps 是否传入
      let dispatchProps = mapDispatchToProps
        ? mapDispatchToProps(store.dispatch, this.props) // 将 dispatch 方法和容器组件的 state 传入 mapDispatchToProps
        : {} // 判断 mapDispatchToProps 是否传入
      this.setState({
        allProps: {
          ...stateProps,
          ...dispatchProps,
          ...this.props
        }
      })
    }

    render () {
      // 将 state.allProps 展开以容器组件的 props 传入
      return <WrappedComponent {...this.state.allProps} />
    }
  }
  return Connect
}


小结

至此,我们就很清楚了,原来 React-redux 就是通过 Context 结合 Redux 来实现 React 应用的状态管理,通过 Connect 这个高阶组件来实现展示组件和容器组件的连接的。

原文 http://www.cnblogs.com/alsy/p/12469323.html

链接: https://www.fly63.com/article/detial/8199

React项目实战:react-redux-router基本原理

React项目实战:react-redux-router基本原理,Redux 的 React 绑定库包含了 容器组件和展示组件相分离 的开发思想。明智的做法是只在最顶层组件(如路由操作)里使用 Redux。其余内部组件仅仅是展示性的,所有数据都通过 props 传入。

react关于 Redux与flux的比较学习

flux四大元素:Dispatcher:根据注册派发动作(action),Store: 存储数据,处理数据,Action:用于驱动Dispatcher,View: 用户界面视图。flux的目的:纠正MVC框架的无法禁绝view与model通信的缺点。Redux基本原则:继承Flux基本原则:单向数据流

如何优雅地在React项目中使用Redux

Redux:状态管理工具,与React没有任何关系,其他UI框架也可以使用Redux,react-redux:React插件,作用:方便在React项目中使用Redux,react-thunk:中间件,作用:支持异步action

从 源码 谈谈 redux compose

compose,英文意思 组成,构成。它的作用也是通过一系列的骚操作,实现任意的、多种的、不同的功能模块的组合,用来加强组件。很容易实现功能的组合拼装、代码复用;可以根据需要组合不同的功能;

Redux与它的中间件:redux-thunk,redux-actions,redux-promise,redux-sage

这里要讲的就是一个Redux在React中的应用问题,讲一讲Redux,react-redux,redux-thunk,redux-actions,redux-promise,redux-sage这些包的作用和他们解决的问题。

redux中间件的原理_深入理解 Redux 中间件

最近几天对 redux 的中间件进行了一番梳理,又看了 redux-saga 的文档,和 redux-thunk 和 redux-promise 的源码,结合前段时间看的redux的源码的一些思考,感觉对 redux 中间件的有了更加深刻的认识,因此总结一下

react-redux 的使用

类似于 Vue,React 中组件之间的状态管理 第三方包为:react-redux。react-redux 其实是 Redux的官方React绑定库,它能够使你的React组件从Redux store中读取数据,并且向store分发actions以更新数据。

如何使用useReducer Hook?

看到“reducer”这个词,容易让人联想到Redux,但是在本文中,不必先理解Redux才能阅读这篇文章。咱们将一起讨论“reducer”实际上是什么,以及如何利用useReducer来管理组件中的复杂状态,以及这个新钩子对Redux意味着什么?

带着问题看React-Redux源码

我在读React-Redux源码的过程中,很自然的要去网上找一些参考文章,但发现这些文章基本都没有讲的很透彻,很多时候就是平铺直叙把API挨个讲一下,而且只讲某一行代码是做什么的,却没有结合应用场景和用法解释清楚为什么这么做。

React Hooks 是不能替代 Redux 的

我的许多同事最近通过各种方式问同一类问题:“如果我们开始用 hook 后,那还有必要用 Redux 吗?”“React hook 不是让 Redux 过时了吗?那只用 Hooks 就可以做 Redux 所有能做的事了吧?”

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!