English Document address

The author added some code as he understood it

Connect: Use mapStateToProps to extract data

MapStateToProps is the first parameter to connect and is used to select part of the data required by the Connected Component from the Store. MapStateToProps is often referred to simply as mapState.

Each time the state in the Store changes, mapStateToProps is called. The mapStateToProps accepts the entire Store and returns the data objects that the component needs


Define mapStateToProps

MapStateToProps should be defined as a function

function mapStateToProps(state, ownProps?)
Copy the code

The first argument is called state, and the second, optional, called ownProps, returns a plain object containing the data required by the Connected Component

MapStateToProps is passed as the first parameter to connect and is called every time the state in the store changes.

If you don’t want to subscribe to the store in the component, just pass NULL or undefined at the first parameter of connect (the location of mapStateToProps)

The parameters of the mapStateToProps

  1. state

The first parameter to mapStateToProps represents the state stored in the entire Redux store (its value is the same as that returned by store.getState()). Therefore, the first parameter is also called state. (Cannot be called store because it is a “state value “and not a “store instance”)

// TodoList.js
function mapStateToProps(state) {
  const { todos } = state
  return { todoList: todos.allIds }
}

export default connect(mapStateToProps)(TodoList)
Copy the code
// todolist.js
export default connect(
	state= > ({todoList:state.todos.allIds})
)(TodoList)
Copy the code
  1. OwnProps (Optional)

If your component wants to use its props data to retrieve data from the store, you can use the second parameter ownProps to define the mapStateProps function. OwnProps contains all props provided to the Wrapper Component generated by Connect

// Todo.js
function mapStateToProps(state, ownProps) {
  const { visibilityFilter } = state
  // ownProps is similar to {"id" : 123}
  const { id } = ownProps
  const todo = getTodoById(state, id)
  // Todo.js component extra received
  return { todo, visibilityFilter }
}

// A parent component is then rendered
;<ConnectedTodo id={123} />
// Your component will receive props. Id, props. Todo, and props. VisibilityFilter
Copy the code

You do not need to include the value from ownProps in the object returned from mapStateToProps. Because CONNECT automatically integrates these different props into a final set of props

The Return of mapStateProps

The mapStateProps returns a plain object containing the data needed for the component on which it is located

  • Each field in the object will be the props in the component it is in
  • The value in the field will be used to determine if your component needs to be rerendered
function mapStateToProps(state) {
  return {
    a: 42.todos: state.todos,
    filter: state.visibilityFilter,
  }
}

// The component receives props. A, props. Todos, and props. Filter
Copy the code
  • Pay attention to

In advanced scenarios where you need more control over rendering performance, mapStateToProps can also return a function. In this case, this function is used as the final mapStateToProps for a particular component instance. You can do per-instance memoization, see Advanced Usage:Computting Derived Data for details


MapStateToProps Usage Guide

letmapStateToPropsrestoreStoreThe data in the

The mapStateToProps function should do more than just return state.someslice. They are responsible for reshaping the data in the Store to fit the needs of that component. This might include returning a value as a specific props name, combining data fragments from different parts of the state tree, and transforming store data in different ways.

useselector functionExtract and transform data

The use of selector functions to help encapsulate the process of extracting values from specific locations in the state tree is strongly encouraged. Memoized selector functions also play a key role in improving application performance (see Advanced Usage: Computing Derived Data page)

mapStateToPropsFunctions should be fast

Every time the Store changes, all mapStateToProps functions for all connected components run. Therefore, your mapStateToProps function should run as fast as possible. This also means that the slow mapStateToProps function can be a potential bottleneck for your application.

As part of the idea of “re-shaping data”, the mapStateToProps function often needs to transform data in various ways (such as filtering arrays, mapping arrays of ids to their corresponding objects, or extracting pure JS values from Immutable. These transformations are often time consuming, both in terms of the cost of performing the transformation and whether components are rerendered as a result. If performance is an issue, be sure to run these transformations only if the input values change.

mapStateToPropsThe functions should be pure and synchronous

Like the Redux Reducer, the mapStateToProps function should always be 100% pure and synchronous. It should only accept state (and ownProps) as parameters and return the data the component needs as props without changing those parameters. It should not be used to trigger asynchronous behavior, such as AJAX calls to retrieve data, and functions should not be declared asynchronous.


mapStateToPropsAnd performance

The return value determines whether or not your component is re-rendered

React Redux internally implements the shouldComponentUpdate method so that wrapper components are rerendered exactly when the data required by the component changes.

By default, React Redux determines whether the content of the object returned from mapStateToProps is different by using a === comparison (a “shallow equality” check) on each field of the returned object. If any of the fields change, your component will be re-rendered so that it can receive the updated values as props. Note that returning mutated objects with the same reference is a common error (possibly caused by reducers being impure functions) that can cause your component to not rerender as expected.

(state) => stateProps (state, ownProps) => stateProps
Startup time of mapStateToProps State changes in store The state in store changes or any of the ownProps fields changes
Component re-rendering time Any field in stateProps changed Any field in stateProps changed or any field in ownProps changed

Return a new object reference only when needed

React Redux does a shallow comparison to see if the result of mapStateToProps has changed. It’s easy to accidentally return a new object or array reference every time, causing your component to re-render even if the data is actually the same.

Many common operations cause new object or array references to be created:

  • Create a new array using somearray.map () or somearray.filter ()
  • Merge arrays using array.concat
  • Slice selects a portion of the array using array.slice
  • Copy values using object.assign
  • Copy values with extended operators {… oldState, … newData }

Place these operations in a memorized selector function to ensure that they are run only when the input value changes. This also ensures that mapStateToProps will still return the same result value as before if the input value has not changed, and connect can skip re-rendering.

Only perform expensive operations when data changes

Converting data is often expensive (and often results in the creation of new object references). You should rerun these complex transformations only when the relevant data changes.

There are several ways to solve this problem:

  • Some transformations can be calculated in Action Creator or Reducer, and the transformed data can be saved in the Store
  • Conversion can also be done in the render() method of the component
  • If the conversion is really needed inmapStateToPropsFunction, then we recommend using the memorized selector function to ensure that the conversion runs only when the input value changes.

Immutable. Js performance problem

  • Immutable. Js author Lee Byron on Twitter explicitly recommends avoiding toJS when considering performance:
  • Performance tip for # imMutableJS: Avoid.tojs ().toobject () and.toarray () all slow full copy operations that render structure sharing useless.


Behavior and traps

If Store State is the same, mapStateToProps will not run

The wrapper component generated by Connect subscribes to the Redux Store. Each time an action is dispatched, it calls store.getState() and checks to see if lastState === currentState. If the two State values are the same by reference, it does not re-run your mapStateToProps function because it assumes that the rest of the Store State has not changed either.

The Redux combineReducers utility function tries to optimize for this. If neither slice Reducers returns a new value, then combineReducers returns the old state object instead of the new value. This means that the variation in the Reducer causes the root state object to not be updated and therefore the UI will not be re-rendered.


We are Dispatching the operation behaviors using mapDispatchToProps.

As the second parameter passed to connect, mapDispatchToProps is used to dispatch the operation to the Store.

Dispatch is a function of Redux Store. You call store.dispatch to schedule an action. This is the only way to trigger a state change.

With React Redux, your components will never directly access the Store — Connect will do it for you. React Redux gives you two ways to have components schedule operations:

  • By default, a connected Component receives props. Dispatch and can schedule actions on its own.
  • connectCan accept a name calledmapDispatchToPropsParameter, which allows you to create functions that are scheduled on invocation and pass those functions to your components as props.

The mapDispatchToProps function is often referred to simply as mapDispatch, but the actual variable name used can be whatever you want.


Dispatching method

Dispatch as a Prop

If you do not specify a second parameter to connect(), your component will receive dispatch by default. Such as:

connect()(MyComponent)

connect(null.null)(MyComponent)

connect(mapStateToProps /** no second argument */)(MyComponent)
Copy the code

Once you connect your component in this way, your component will receive props. Dispatch. You can use it to dispatch operations to the Store.

function Counter({ count, dispatch }) {
  return (
    <div>
      <button onClick={()= > dispatch({ type: 'DECREMENT' })}>-</button>
      <span>{count}</span>
      <button onClick={()= > dispatch({ type: 'INCREMENT' })}>+</button>
      <button onClick={()= > dispatch({ type: 'RESET' })}>reset</button>
    </div>)}Copy the code

Provides the mapDispatchToProps parameter

Provides mapDispatchToProps that allows you to specify operations that the component may need to dispatch. It allows you to provide motion scheduling functionality as an item.

Therefore, you can call props.increment() directly instead of calling props. Dispatch (() => increment()). There are several reasons why you might want to do this.

  1. More declarative

First, encapsulating the scheduling logic in functions makes the implementation more declarative. The main point Dispatching Dispatching an action and asking the Redux Store to handle the data stream is “how the behavior is implemented”, not “what it does”.

A good example is dispatching an action when a button is clicked. A directly connected button may not make sense conceptually, and there is no button reference for Dispatch.

// Button needs to be aware of "dispatch"
<button onClick={() = > dispatch({ type: "SOMETHING"})} / >// Button is unaware of "dispatch",
<button onClick={doSomething} />
Copy the code

Once you have packaged all of the Action Creators with an action dispatch function, this component does not need to be dispatched. Therefore, if you define your own mapDispatchToProps, connected components will no longer receive scheduling.

  1. Passes the action scheduling logic to the (unconnected) child component

In addition, you gain the ability to pass action dispatch functionality to unconnected components. This allows more components to perform dispatch operations without being “aware” of Redux’s existence.

// Pass toggleTodo to the child component
// Enable Todo to schedule toggleTodo actions
const TodoList = ({ todos, toggleTodo }) = > (
  <div>
    {todos.map((todo) => (
      <Todo todo={todo} onClick={toggleTodo} />
    ))}
  </div>
)
Copy the code

That’s what React Redux’s Connect does — it encapsulates the logic for talking to the Redux Store.


mapDispatchToPropsTwo forms of

The mapDispatchToProps parameter can take two forms. The functional form allows for more customization, and the object form is easy to use.

  • Function form: Allows for more customization, access to dispatches and optional ownProps
  • Object shorthand: More declarative and easier to use

Note: We recommend using the object form of mapDispatchToProps unless you specifically need to customize the scheduling behavior in some way.


mapDispatchToPropsThe functional form of

Defining mapDispatchToProps as functions gives you the greatest flexibility in customizing the functions that components receive and how they schedule operations. You can visit Dispatch and ownProps. You can take advantage of this opportunity to write custom functions to be called by your wired component.

parameter

  • dispatch
  • ownProps (optional)
  1. dispatch

The mapDispatchToProps function will be called with Dispatch as the first argument. It is typically used by returning a new function that calls Dispatch () within it, and can be passed directly to a plain Action Object or the result of an Action Creator.

const mapDispatchToProps = (dispatch) = > {
  return {
    // Send out ordinary actions
    increment: () = > dispatch({ type: 'INCREMENT' }),
    decrement: () = > dispatch({ type: 'DECREMENT' }),
    reset: () = > dispatch({ type: 'RESET'}}}),Copy the code

You can also forward parameters to Action Creator:

const mapDispatchToProps = (dispatch) = > {
  return {
    // An explicit forward parameter
    onClick: (event) = > dispatch(trackClick(event)),

    // Implicit forwarding parameters
    onReceiveImpressions: (. impressions) = >
      dispatch(trackImpressions(impressions)),
  }
}
Copy the code
  1. OwnProps (Optional)

If your mapDispatchToProps function is declared with two arguments, it will be called with Dispatch as the first argument and the props passed to the Connected Component as the second argument, And is called again when the Connected Component receives new props.

This means that you can do this when a component’s props changes, rather than rebinding the new props to Action Dispatchers when the component is re-rendered.

  • The new props is bound to the component load
render() {
  return <button onClick={()= > this.props.toggleTodo(this.props.todoId)} />
}

const mapDispatchToProps = dispatch= > {
  return {
    toggleTodo: todoId= > dispatch(toggleTodo(todoId))
  }
}
Copy the code
  • The new props is bound in the props change
render() {
  return <button onClick={()= > this.props.toggleTodo()} />
}

const mapDispatchToProps = (dispatch, ownProps) = > {
  return {
    toggleTodo: () = > dispatch(toggleTodo(ownProps.todoId))
  }
}
Copy the code

Return

The mapDispatchToProps function should return a plain object:

  • Each field in the object will be a separate prop for that component, and the value should normally be a function that dispatches action when called.
  • If you use action Creators in your schedule, the agreement will simply name the Key the same name as the action creator:
const increment = () = > ({ type: 'INCREMENT' })
const decrement = () = > ({ type: 'DECREMENT' })
const reset = () = > ({ type: 'RESET' })

const mapDispatchToProps = (dispatch) = > {
  return {
    // Action Creators returned dispatching Actions
    increment: () = > dispatch(increment()),
    decrement: () = > dispatch(decrement()),
    reset: () = > dispatch(reset()),
  }
}
Copy the code

The return value of the mapDispatchToProps function is merged into the Connected Component as props. You can call the return value directly to schedule the component’s Actions.

function Counter({ count, increment, decrement, reset }) {
  return (
    <div>
      <button onClick={decrement}>-</button>
      <span>{count}</span>
      <button onClick={increment}>+</button>
      <button onClick={reset}>reset</button>
    </div>)}Copy the code

withbindActionCreatorsdefinemapDispatchToPropsfunction

Wrapping these functions manually was tedious, so Redux provided a function to simplify it – bindActionCreators.

BindActionCreators converts an object with a value of ActionCreators into an object with the same key, but each ActionCreators is wrapped in a Dispatch call so that they can be called directly.

BindActionCreators accepts two parameters:

  • A function (an Action Creator) or an object (each field is an action creator)
  • dispatch

The wrapper function generated by bindActionCreators will automatically forward all their parameters, so you don’t need to do this manually.

import { bindActionCreators } from 'redux'

const increment = () = > ({ type: 'INCREMENT' })
const decrement = () = > ({ type: 'DECREMENT' })
const reset = () = > ({ type: 'RESET' })

// Bind a single Action creator
// returns (... args) => dispatch(increment(... args))
const boundIncrement = bindActionCreators(increment, dispatch)

// Bind an object consisting of Action Creators
const boundActionCreators = bindActionCreators(
  { increment, decrement, reset },
  dispatch
)
// returns
/ / {
//   increment: (...args) => dispatch(increment(...args)),
//   decrement: (...args) => dispatch(decrement(...args)),
//   reset: (...args) => dispatch(reset(...args)),
// }
Copy the code

Use bindActionCreators in the mapDispatchToProps function

import { bindActionCreators } from 'redux'
// ...

function mapDispatchToProps(dispatch) {
  return bindActionCreators({ increment, decrement, reset }, dispatch)
}

// A component accepts props. Increment, props. Decrement, props. Reset
connect(null, mapDispatchToProps)(Counter)
Copy the code

Manual injection dispatch

If the mapDispatchToProps parameter is provided, the component will no longer receive the default dispatch. You can bring it back by manually adding it to the return of mapDispatchToProps, although most of the time you don’t need to do this:

import { bindActionCreators } from 'redux'
// ...

function mapDispatchToProps(dispatch) {
  return{ dispatch, ... bindActionCreators({ increment, decrement, reset }, dispatch), } }Copy the code


willmapDispatchToPropsDefined as an object

You’ve already seen that the setup for scheduling Redux actions in the React component follows a very similar process: define an action creator and wrap it around another one that looks like (… args) => dispatch(actionCreator(… Args)), and then pass the wrapper function to the component as a prop.

As is common, connect supports the “object shorthand” form of the mapDispatchToProps parameter: If you pass in an object consisting of ActionCreators, rather than a function, Connect will automatically call bindActionCreators internally for you.

It is recommended to always usemapDispatchToPropsObject shorthand form of

Note:

  • Each field of the mapDispatchToProps object is assumed to be an Action Creator
  • Your component will no longer receive dispatches as props
// React Redux automatically does the following code for you
;(dispatch) = > bindActionCreators(mapDispatchToProps, dispatch)

// So you just want to abbreviating mapDispatchToProps to
const mapDispatchToProps = {
  increment,
  decrement,
  reset,
}
Copy the code

Since the actual name of the variable is up to you, you might want to give it a name similar to actionCreators, or even define the object inline when calling Connect:

import { increment, decrement, reset } from './counterActions'

const actionCreators = {
  increment,
  decrement,
  reset,
}

export default connect(mapState, actionCreators)(Counter)

// or
export default connect(mapState, { increment, decrement, reset })(Counter)
Copy the code


Q&A

Why didn’t my component receive dispatch?

TypeError: this.props.dispatch is not a function
Copy the code

This is a common error that occurs when you try to call this.props. Dispatch, but dispatch is not injected into your component.

Dispatch is injected into your component only if:

  1. Did not provide amapDispatchToProps

The default mapDispatchToProps is just dispatch => ({dispatch}). If you do not provide mapDispatchToProps, the scheduling described above will be provided.

// In other words, it will be like this
// component receives `dispatch`
connect(mapStateToProps /** no second argument*/)(Component)
Copy the code
  1. The custom mapDispatchToProps function returns a concrete containing dispatch

Dispatch can be restored by providing a custom mapDispatchToProps function

const mapDispatchToProps = (dispatch) = > {
  return {
    increment: () = > dispatch(increment()),
    decrement: () = > dispatch(decrement()),
    reset: () = > dispatch(reset()),
    dispatch,
  }
}
Copy the code

Or, use bindActionCreators:

import { bindActionCreators } from 'redux'

function mapDispatchToProps(dispatch) {
  return{ dispatch, ... bindActionCreators({ increment, decrement, reset }, dispatch), } }Copy the code

Can I have mapDispatchToProps without mapStateToProps in Redux?

Yes! You can skip the first argument by passing undefined or null, and the component will not subscribe to store and still receive dispatch props defined by mapdispatchToProps

connect(null, mapDispatchToProps)(MyComponent)
Copy the code