Understanding the Redux architecture We learned the basic concepts and workflow of the Redux architecture such as Store, Action and reducers. Redux can be used in combination with other front-end libraries, and react-Redux is a library that combines Redux with React.js.

Context

In React applications, data is passed top-down through the props property. If there are many components in our application that need to share the same data state, we can use the idea of state promotion to elevate the common state to their common parent. But we know that doing so is tedious, and the code is difficult to maintain. Context is considered, which provides a way to pass data across the component tree without manually adding props for each layer of components. That is, if a context is set in a component, its children can access its contents directly, rather than passing them through intermediate components, like a global variable.

In App -> Toolbar -> ThemedButton use the props property to pass the theme, and the Toolbar acts as an intermediate component to pass the theme from the App component to the ThemedButton component.

class App extends React.Component {
  render() {
    return<Toolbar theme="dark" />; }} Function Toolbar(props) {// The Toolbar component accepts an additional "theme" property, which is then passed to the ThemedButton component. // If every single button in your application needed to know the theme value, it would be a hassle, because you would have to pass this value through all the components. return ( <div> <ThemedButton theme={props.theme} /> </div> ); } class ThemedButton extends React.Component { render() { return <Button theme={this.props.theme} />; }}Copy the code

Using context, you can avoid passing props through intermediate elements

// Context allows us to pass values deep into the component tree without explicitly traversing each component.
// Create a context for the current theme (" light "is the default).
const ThemeContext = React.createContext('light');

class App extends React.Component {
  render() {
    // Use a Provider to pass the current theme to the following component tree.
    // Any component, no matter how deep, can read this value.
    // In this case, we pass "dark" as the current value.
    return( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> ); }} // Intermediate components no longer have to specify the theme to pass down. function Toolbar(props) { return ( <div> <ThemedButton /> </div> ); } class ThemedButton extends react.ponent {// Specify contextType to read the current theme context. // React will find the nearest theme Provider and use its value. // In this case, the current theme value is "dark". static contextType = ThemeContext; render() { return <Button theme={this.context} />; }}Copy the code

While solving the problem of state passing introduces two new problems.

  1. The context we introduced is like a global variable, where the data can be changed at will by the quilt component, possibly causing the program to run unpredictably.

  2. Context greatly enhances the coupling between components, resulting in poor reusability of components. For example, the ThemedButton component relies on context data, resulting in poor reusability.

Redux provides the ability to manage shared state, so we can manage the context using Redux. The first problem is solved.

The Provider component

React-redux provides a Provider component that uses the React context feature to place a store in the context, enabling all components under the component to directly access the Store. The general implementation is as follows:

class Provider extends Component {
  The method getChildContext is the procedure for setting the context. The object it returns is the context, and all child components can access this object
  getChildContext() {
    return {
      store: this.props.store
    };
  }
  render() {
    return this.props.children;
  }
}

Provider.childContextTypes = {
  store: React.PropTypes.object
}
Copy the code

We can use the Provider component as the root component to wrap our application so that the entire application component can access the data inside

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'))Copy the code

Demonstrates the (Dumb Components) component and the container (Smart Components) component

Remember our second question? Components become unreusable because of the intrusion of context. To solve this problem, React-Redux divides all components into two broad categories: presentation components and container components.

Presentation components A presentation component has several characteristics

  1. The component is only responsible for the PRESENTATION of the UI, without any business logic

  2. The component has no state, that is, this.state is not used

  3. The component data is determined only by props

  4. The component does not use any of Redux’s apis

Presentation components, like pure functions, return results only depending on its parameters, and there are no side effects in the execution process, which makes people feel very reliable and can be trusted to use.

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>)}}Copy the code

A Title component like this one is a presentation component, and the result of the component is entirely determined by the Title attribute passed in externally.

Container components Container components have the opposite characteristics

  1. Components manage data and business logic, not UI presentation

  2. Components have internal state

  3. The component’s data is retrieved from Redux state

  4. Use Redux’s API

You can write container components directly using store.subscribe(), but this is not recommended because you can’t use the performance optimization provided by React-Redux.

React-redux specifies that all display components are provided by the user and container components are automatically generated by Connect () of React-Redux.

High-level component Connect

React-redux provides the connect method to generate container components from our defined presentation components. The connect function takes a presentation component argument and returns another container component. So connect is actually a higher-order component (a higher-order component is a function that passes it a component and returns a new component).

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

export default connect()(Header);
Copy the code

In the above code, the Header is a presentation component, which becomes a container component after connect, and is exported as a module. The container component does not define any business logic, so it cannot do anything. We can define our business logic using mapStateToProps and 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);
Copy the code

The mapStateToProps tells connect that we want to get the title data in the state, and eventually the title data will be passed to the title presentation component as props.

MapStateToProps also subscribes to the Store, which automatically recalculates the parameters of the presentation component whenever state is updated, triggering a re-rendering of the presentation component.

The mapDispatchToProps tells connect that we need the Dispatch action, and eventually onChangeColor passes the Title presentation component as the props callback.

The Connect component is roughly implemented as follows

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

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

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

    _updateProps () {
      const { store } = this.context
      let stateProps = mapStateToProps
        ? mapStateToProps(store.getState(), this.props) // Pass the state of the Store and the state of the container component to mapStateToProps
        : {} // Check whether mapStateToProps is passed
      let dispatchProps = mapDispatchToProps
        ? mapDispatchToProps(store.dispatch, this.props) // Pass the dispatch method and the state of the container component to mapDispatchToProps
        : {} // Check whether mapDispatchToProps is passed
      this.setState({
        allProps: {... stateProps, ... dispatchProps, ... this.props } }) } render () {// Expand the state.allProps to the props of the container component
      return <WrappedComponent {. this.state.allProps} / >
    }
  }
  return Connect
}
Copy the code

summary

Redux uses Context and redux to manage React state. Redux uses Connect to Connect display components to container components.

For more exciting content, please pay attention to the wechat public number ~