preface

React-redux divides all components into two broad categories: UI components (Presentational Components) and container Components (Container Components).

UI components

  • Only responsible forUIIs rendered without any business logic;
  • No state (that is, not usedthis.stateThis variable);
  • All data is defined by parameters (this.props);
  • Without using anyReduxAPI;

Container components

  • Responsible for managing data and business logic, but not responsibleUIPresent;
  • With internal state;
  • useReduxAPI;

The UI component is responsible for rendering the UI, and the container component is responsible for managing the data and logic.

What if a component has both UI and business logic? The answer is to break it down into the following structure: a container component on the outside and a UI component on the inside. The former is responsible for communicating with the outside world, passing the data to the latter, who renders the view.

React-redux specifies that all UI components are provided by the user, while container components are automatically generated by React-Redux. In other words, the user takes care of the visual layer, leaving state management to it.

Connect () React-redux provides the connect method for generating container components from UI components. Connect means to connect the two components together.

import { connect } from 'react-redux'
const VisibleTodoList = connect()(TodoList);
Copy the code

The above VisibleTodoList is the container component automatically generated by the Connect method. But the business logic needs to be defined for the component to make sense.

import { connect } from 'react-redux'
const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)
Copy the code

The connect method accepts two parameters: mapStateToProps and mapDispatchToProps. They define the business logic of the UI components. The former is responsible for the input logic, which maps state to the UI component’s parameters (props), and the latter is responsible for the output logic, which maps user actions on the UI component to actions.

mapStateToProps(a)

It is a function that establishes a mapping between the (external) state object and the (UI component’s) props object.

The mapStateToProps should return an object in which each key-value pair is a mapping.

const mapStateToProps = (state) => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}
Copy the code

As mentioned earlier, mapStateToProps is a function that takes state as an argument and returns an object. This object has a todos attribute that represents the UI component’s nameless parameter, followed by getVisibleTodos, which is also a function that calculates the toDOS value from state.

MapDispatchToProps () mapDispatchToProps is the second parameter of the connect function that maps UI component parameters to the Store. dispatch method. It defines which user actions should be passed to the Store as actions. It can be a function or an object.

When mapDispatchToProps is a function, two parameters are given: Dispatch and ownProps (the props object of the container component).

const mapDispatchToProps = (
  dispatch,
  ownProps
) => {
  return {
    onClick: () => {
      dispatch({
        type: 'SET_VISIBILITY_FILTER', filter: ownProps.filter }); }}; }Copy the code

As you can see from the code above, mapDispatchToProps, as a function, should return an object where each key-value pair is a mapping that defines how the UI component’s parameters emit actions.

When mapDispatchToProps is an object, each of its key names is a parameter of the same name as the UI component. The key value should be a function that is treated as an Action Creator, and the returned Action is automatically emitted by Redux.

const mapDispatchToProps = {
  onClick: (filter) => {
    type: 'SET_VISIBILITY_FILTER',
    filter: filter
  };
}
Copy the code

After the component connect method generates the container component, it needs to get the state object from the container component to generate the UI component’s parameters.

React-redux provides a Provider component that lets container components get state.

import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'

let store = createStore(todoApp);

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

In the code above, the Provider wraps a layer around the root component so that all child components of the App get state by default.

Demo: Simple counters

Below is a counter component that is a pure UI component.

class Counter extends Component {
  render() {
    const { value, onIncreaseClick } = this.props
    return ( 
       
{value}
)
}}
Copy the code

In the code above, the UI component has two parameters: value and onIncreaseClick. The former needs to be computed from state, and the latter needs to issue the Action.

Next, define the value to state mapping and the onIncreaseClick to Dispatch mapping.

function mapStateToProps(state) {
  return {
    value: state.count
  }
}

function mapDispatchToProps(dispatch) {
  return {
    onIncreaseClick: () => dispatch(increaseAction)
  }
}

// Action Creator
const increaseAction = { type: 'increase' }
Copy the code

The container component is then generated using the CONNECT method.

const App = connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter)
Copy the code

Then, define the Reducer of this component.

// Reducer
function counter(state = { count: 0 }, action) {
  const count = state.count
  switch (action.type) {
    case 'increase':
      return { count: count + 1 }
    default:
      return state
  }
}
Copy the code

Finally, a Store object is generated and a Provider is used to cover the root component.

import { loadState, saveState } from './localStorage';

const persistedState = loadState();
const store = createStore(
  todoApp,
  persistedState
);

store.subscribe(throttle(() => {
  saveState({
    todos: store.getState().todos,
  })
}, 1000))

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

The complete code is as follows:

import React, { Component } from 'react'
import PropTypes from 'prop-types'   // Type check
import ReactDOM from 'react-dom'
import { createStore } from 'redux'
import { Provider, connect } from 'react-redux'

// Define the counter component
class Counter extends Component {
  render() {
    const { value, onIncreaseClick } = this.props
    // const value = this.props.value
    return (
      <div>
        <span>{value}</span>
        <button onClick={onIncreaseClick}> +1</button>
      </div>
    )
  }
}
// Type check the props accepted by the Counter component
Counter.propTypes = {
  value: PropTypes.number.isRequired,   // Number type required, no warning provided
  onIncreaseClick: PropTypes.func.isRequired // The function type is required
}

// Action  
const increaseAction = { type: 'increase' }

// Reduce the state based on the existing state and obtain the new state according to the action
function counter(state = { count: 0 }, action) {
  const count = state.count
  switch (action.type) {
    case 'increase':
      return { count: count + 1 }
    default:
      return state
  }
}

Create a store using createStore() according to the reducer function
const store = createStore(counter)

// Map state to props of the Counter component
function mapStateToProps(state) {
  return {
    value: state.count
  }
}

// Map action to props of the Counter component
function mapDispatchToProps(dispatch) {
  return {
    onIncreaseClick: () => dispatch(increaseAction)
  }
}

// Pass the above two function arguments to change the Counter component into the App component
const App = connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter)

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

Develop reading