Connect: Extract data using mapStateToProps

As the first parameter passed to connect, mapStateToProps selects part of the data needed by the connected component from the store. Often denoted by the abbreviation mapState.

  • Called whenever store State changes

  • Receives the entire Store state and returns the data object required by the component

The statement mapStateToProps

MapStateToProps should be declared as a method:

function mapStateToProps(state, ownProps?)
Copy the code

He receives state as the first argument, ownProps as the optional second argument, and returns a pure object of the data needed by the connected component.

This method should be passed to Connect as the first argument and will be called every time the Redux Store state changes. If you do not want to subscribe to the store, pass null or undefined as the first parameter of connect instead of mapStateToProps.

Whether **mapStateToProps is declared using the function keyword (function mapState(state) {})** or as an arrow function **(const mapState = (state) => {})** It all works the same way.

parameter

  1. state

  2. OwnProps (Optional)

state

The first argument to mapStateToProps is the entire Redux Store State object (the same value returned by the store.getState() method). So the first parameter is usually named state (you can choose something else, but store is not recommended — it’s a state value, not a store instance).

At a minimum, the mapStateToProps method passes the state argument.

// TodoList.js 

function mapStateToProps(state) {
  const { todos } = state;
  return { todoList: todos.allIds };
};
    
export default connect(mapStateToProps)(TodoList);
Copy the code

ownProps**** (Optional)

If your component needs its props data to retrieve data from the store, you can pass in the second parameter, ownProps. This parameter will contain all the props passed to the wrapper component generated by CONNECT.

// Todo.js function mapStateToProps(state, ownProps) { const { visibilityFilter } = state; const { id } = ownProps; const todo = getTodoById(state, id); Return {todo, visibilityFilter}; }; <ConnectedTodo ID ={123} /> // Your component receives props. Id, props. Todo, and props. VisibilityFilterCopy the code

You don’t need to add the values of ownProps to the object returned by mapStateToProps, Connect will automatically combine the different props into a final prop set.

The return value

Your mapStateToProps method should return a pure object containing the data used by the component:

  • Each field in the object will act as a prop for your component

  • The value in the field is used to determine if your component needs to be rerendered

Such as:

Function mapStateToProps(state) {return {a: 42, todos: state.todos, filter: state.visibilityFilter}} A, props. Todos, and props. FilterCopy the code

Note: in advanced scenarios where you may need more control over rendering performance, mapStateToProps can also return a method. In this case, that method is returned as the final mapStateToProps for a particular component instance. This way, you can memoization each instance. Refer to the advanced Usage section for more information. Also see PR #279 and the added tests above. But most apps don’t need to do this at all

Use guide

Let mapStateToProps manipulate the data pulled from the store

The mapStateToProps method can, and should, do more than just return a state.someslice. It is their responsibility to transform the store data needed to build it. For example, returning the value of a specific prop name, taking pieces of data from different parts of the state tree and merging them into a whole, and transforming stores in different ways.

Use the Selector method to extract and convert data

We strongly recommend using the selector method to encapsulate and extract values at specific locations in the state tree. The Memoized Selector method also plays a key role in improving application performance. (See the following section of this page: Advanced Usage: Performance for more details on why and how selectors are used.)

The mapStateToProps method should be fast enough

Once the store changes, all mapStateToProps methods in all connected components run. Therefore, your mapStateToProps method must be fast enough. This also means that the slow mapStateToProps method can be a potential bottleneck in your application.

As part of the idea of “remaking data,” the mapStateToProps method often needs to transform data in a variety of ways (such as filtering arrays, traversing IDs arrays mapped to their corresponding objects, or extracting pure JS values from Immutable. These conversions are often expensive, not only in terms of the cost of running the conversions, but also in determining whether or not the components will eventually be updated. If you do need to worry about performance, make sure that your transformations only happen when the input values change.

The mapStateToProps method should be clean and synchronized

As Redux Reducer, a mapStateToProps method should be 100% clean and synchronous. He should only take state (and ownProps) as arguments, and then return the data that the component needs as props. It should not trigger any asynchronous operations, such as AJAX requests for data, nor should methods be declared async.

MapStateToProps and performance

The return value determines whether your component needs to be updated

Inside react-Redux, the shouldComponentUpdate method is implemented to accurately rerender if the data used by the component changes. By default, react-Redux uses “===” to compare each field of the object returned by mapStateToProps to see if anything has changed. Whenever a field changes, your component will be re-rendered to receive the updated props values. Note that returning a mutated object with the same reference is a common mistake, as it will cause your component to not rerender as expected.

To summarize the behavior of the component wrapped in the CONNECT method that passed the mapStateToProps parameter to extract data from the store:

state=>stateProps

(state,ownProps)=>stateProps

mapStateToProps

The store state changes

Store State changes or any ownProps field changes

Component rerenders the condition

Any stateProps field changes

Any stateProps field changes or any ownProps field changes

Return a new object reference only when needed

React-redux performs a shallow comparison to check if the result of mapStateToProps has changed. Returning a new object or array reference is easy to do, but will cause your component to re-render even if the data remains unchanged.

Many common operations return a new object or array reference:

  • Create a new array: use somearray.map () or somearray.filter ()

  • Merge arrays: array.concat

  • Intercepting arrays: array.slice

  • Copy value: Object.assgin

  • Use the extension operator: {… oldState,… newData}

Putting these operations in memeoized Selector functions ensures that they only run when the input value changes. This also ensures that if the input value does not change, mapStateToProps still returns the same value as before, and connect can then skip the rendering process.

Run costly operations only when data changes

Converting data is often expensive (and often creates a new object reference). To make your mapStateToProps method fast enough, you should re-run these complex transformations only when the relevant data changes.

There are several ways to do this:

  • Some transformations can be done in the action creation function or reducer, and the transformed data can then be stored in a store

  • Conversion can also be done in the Render () life cycle of a component

  • If the transformation must be done in the mapStateToProps method, we recommend memoized Selector functions to ensure that the transformation only occurs when the input data changes.

Consider Immutable. Js properties

Lee Byron, author of Immutable. Js, explicitly advises avoiding toJS if you’re thinking about performance in a post on Twitter

Perf tip for #immutablejs: avoid .toJS() .toObject() and .toArray() all slow full-copy operations which render structural sharing useless.

There are a few other suggestions for improving Immutable. Js performance – see the list of links below.

Behavior and Summary

MapStateToProps will not run with the same Store state

The wrapper component generated by Connect subscribes to the Redux Store. Whenever an action is distributed, it calls store.getState() and checks to see if lastState===currentState. If the two state references are exactly the same, then mapStateToProps will not run because the component assumes that your remaining store state has not changed.

Redux’s combineReducers function will try to optimize it. If no new value is returned from any reducer, then combineReducers returns the old state object instead of the new one. This means that a mutation in a Reducer will not update the root state object and of course the UI will not be rerendered.

The number of declared parameters affects behavior

When there is only (state), the function is run whenever the root Store State object is different.

When there are two arguments (state,ownProps), the function runs whenever the store state is different, or whenever the wrapper props changes.

This means that ** you should not increase the ****ownProps** parameter unless you really need it, or your mapStateToProps function will run more times than it really needs to run.

There are some extreme cases of this behavior. The number of arguments determines whether the **mapStateToProps accepts the ownProps** parameter.

If the function was previously defined with a command parameter, mapStateToProps will not accept ownProps

function mapStateToProps(state) {
  console.log(state);        // state
  console.log(arguments[1]); // undefined
}
const mapStateToProps = (state, ownProps = {}) => {
  console.log(state);    // state
  console.log(ownProps); // undefined
}
Copy the code

If a previously defined function contains zero or two command parameters, it needs to receive the ownProps argument:

function mapStateToProps(state, ownProps) { console.log(state); // state console.log(ownProps); // ownProps } function mapStateToProps() { console.log(arguments[0]); // state console.log(arguments[1]); // ownProps } function mapStateToProps(... args) { console.log(args[0]); // state console.log(args[1]); // ownProps }Copy the code

Links and References

The tutorial

  • Practical Redux Series, Part 6: Connected Lists, Forms, and Performance

  • Idiomatic Redux: Using Reselect Selectors for Encapsulation and Performance

performance

  • Lee Byron’s Tweet Suggesting to avoid toJS, toArray and toObject for Performance

  • Improving React and Redux performance with Reselect

  • Immutable data performance links

Q&A

  • Why Is My Component Re-Rendering Too Often?

  • Why isn’t my component re-rendering, or my mapStateToProps running

  • How can I speed up my mapStateToProps?

  • Should I only connect my top component, or can I connect multiple components in my tree?

Connect: Distributes actions using mapDispatchToProps

As a second parameter passed to connect, mapDispatchToProps implements the distribution of ACions to the store.

Dispatch is a method of the Redux Store instance where you distribute an action through store.dispatch. This is also the only way to trigger a state change.

With React-Redux, your components no longer need to interact directly with stores — connect will do this for you. React-redux provides two ways to distribute actions:

  • By default, a connected component can receive props. Dispatch and distribute actions itself

  • Connect can accept a mapDispatchToProps as a second parameter, which will allow you to create dispatch call methods and then pass those methods to your component as props.

Dispatching the ways in (Dispatching)

Default: As a prop dispatch

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); // Or 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 distribute actions 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

Provide a mapDispatchToProps parameter

Providing a mapDispatchToProps parameter allows you to specify which actions your component actually needs to distribute. It allows you to supply the action distribution function as props, so instead of calling props. Dispatch (() => increment()), you can use props. Increment (). You do this for several reasons:

More declarative

First, encapsulating the distribution logic in functions makes the overall implementation more declarative. Distributing an action and then letting the Redux Store process the data stream shows how you implement this behavior rather than just caring about what it does.

Distributing an action after clicking a button might be a good example. It doesn’t make sense conceptually to connect a button directly, and a Button doesn’t have dispatch references.

<button onClick={() => dispatch({type: <button onClick={doSomething} />Copy the code

Once you have packaged all the Action Creators functions, your component no longer needs to be dispatched. Therefore, if you define mapDispatchToProps the connected component will no longer receive Dispatch.

Pass the action distribution logic to the child (unconnected) component

In addition, you can now pass down your action distribution functions to child components (which may not be wired). This enables more components to distribute Actions, and they are also insensitive to Redux.

TodoList = ({todos, TodoList, TodoList, TodoList, TodoList, TodoList, TodoList, TodoList, TodoList, TodoList, TodoList, TodoList, TodoList, TodoList, TodoList toggleTodo }) => ( <div> {todos.map(todo => ( <Todo todo={todo} onClick={toggleTodo} /> ))} </div> );Copy the code

That’s what React-Redux’s Connect does — encapsulate the logic of talking to the Redux Store, and you don’t have to worry about it anymore. You should also take full advantage of this in your implementation.

Two forms of mapDispatchToProps

The mapDispatchToProps parameter has two forms: the function form is more customized, and the object form is simpler.

  • Functional form: Higher degrees of freedom, access to Dispatches and optional ownProps

  • Object form: More declarative and easier to use

Note: We recommend using mapDispatchToProps in object form, unless you need to distribute in some custom form

Define mapDispatchToProps as a function

Defining mapDispatchToProps as a function gives you more flexibility in defining what functions your components can receive and how those functions distribute Actions. You have access to both Dispatch and ownProps. You can use this opportunity to write custom functions for your connected components.

parameter

  1. dispatch

  2. OwnProps (Optional)

dispatch

The mapDispatchToProps function is called with Dispatch as the first argument. Typically, you use this argument to return a new function that internally called Dispatch (), and then internally pass either a pure Action object or the return value of the action creator function.

Const mapDispatchToProps => {return {// Increment: () => Dispatch ({type: "INCREMENT" }), decrement: () => dispatch({ type: "DECREMENT" }), reset: () => dispatch({ type: "RESET" }) }; };Copy the code

You may also need to pass some parameters to your action creation function

Const mapDispatchToProps => {return {// Event => Dispatch (trackClick(event)), // onReceiveImpressions: (... impressions) => dispatch(trackImpressions(impressions)) }; };Copy the code

ownProps**** (Optional)

Your mapDispatchToProps function can accept two parameters. The first parameter is dispatchprops, and the props passed to the connected component is the second parameter of the mapDispatchToProps function, which will be called again when the component receives the new props.

This means that you should re-bind the new props to the Action distribution function during the component props change phase, not during the component re-render phase.

<button onClick={() => this.props. ToggleTodo (this.props. TodoId)} />; // const mapDispatchToProps = (dispatch, ownProps) => {toggleTodo: () => dispatch(toggleTodo(ownProps.todoId)); };Copy the code

The return value

Your mapDispatchToProps function should return a pure object.

  • Each object field acts as a separate prop for your component, and the value of the field is usually a function that can be called to distribute the action.

  • If you use an action creation function in dispatch(as opposed to an action in pure object form), it is common convention for the field name to be the same as the name of the action creation function

    const increment = () => ({ type: “INCREMENT” }); const decrement = () => ({ type: “DECREMENT” }); const reset = () => ({ type: “RESET” });

    Const mapDispatchToProps => {return {// Publish actions increment created by Action Creators: () => dispatch(increment()), decrement: () => dispatch(decrement()), reset: () => dispatch(reset()) }; };

The function return value of mapDispatchToProps is merged into your component props. You can call them directly to distribute 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

(See CodeSandbox for the full code for the Counter case)

Define the mapDispatchToProps function using bindActionCreators

Wrapping these functions manually is tedious, so Redux provides a function to simplify this operation.

BindActionCreators converts objects whose values are ActionCreators into new objects with the same key name, but wraps each ActionCreators into a Dispatch call so that they can be called directly. Refer to the story | bindActionCreators

BindActionCreators receives two parameters:

  1. A function (Action Creator) or an object (one Action Creator for each property)

  2. dispatch

The wrapper function generated by bindActionCreators will automatically forward all of its 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 an Action creator // return (... args) => dispatch(increment(... args)) const boundIncrement = bindActionCreators(increment, dispatch); // Bind an action Creators composition object const boundActionCreators = bindActionCreators({increment, Decrement, reset}, dispatch); // increment: (increment) {// increment: (increment); args) => dispatch(increment(... args)), // decrement: (... args) => dispatch(decrement(... args)), // reset: (... args) => dispatch(reset(... args)), // }Copy the code

Using the bindActionCreators function in mapDispatchToProps:

import { bindActionCreators } from "redux"; / /... function mapDispatchToProps(dispatch) { return bindActionCreators({ increment, decrement, reset }, dispatch); } // rement can accept props. Increment, props. Decrement, props. Reset connect(null, mapDispatchToProps)(Counter);Copy the code

Manual injection dispatch

If mapDispatchToProps is provided, the component will no longer receive the default Dispatch. But you can re-inject it into your component by adding dispatch to the return of mapDispatchToProps. In most cases, you don’t need to.

import { bindActionCreators } from "redux"; / /... function mapDispatchToProps(dispatch) { return { dispatch, ... bindActionCreators({ increment, decrement, reset }, dispatch) }; }Copy the code

Define mapDispatchToProps as an object

As you’ve noticed, the process of distributing Redux Actions in the React component is very similar: define the action creation function and wrap it in a form such as… The args) = > dispatch (actionCreator (… Args), and pass that wrapper function as props to your component.

Because this process is so generic, Connect supports an object shorthand for the mapDispatchToProps parameter: If you pass in an object consisting of ActionCreators, rather than a function, Connect will automatically call bindActionCreators internally for you

We suggest dispatching | ** props in this “object shorthand” form, unless you have special reasons to customize the behavior dispatching**.

Notice:

  • Each field of the mapDispatchToProps object is assumed to be an action creation function

  • Your component no longer receives dispatches as a prop

    // React-redux works for you automatically: dispatch => bindActionCreators(mapDispatchToProps, dispatch);

Therefore, our mapDispatchToProps can be abbreviated as:

const mapDispatchToProps = {
  increment,
  decrement,
  reset
};
Copy the code

Since the variable name is up to you, you may want to name it actionCreators or even use an inline object directly when calling Connect:

import {increment, decrement, reset} from "./counterActions"; const actionCreators = { increment, decrement, reset } export default connect(mapState, actionCreators)(Counter); // Export default Connect (mapState, {increment, Decrement, reset})(Counter);Copy the code

Q&A

Why do components no longer receive Dispatches?

That is, an error will be reported:

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

This error is common when you try to call this.props. Dispatch because dispatch is not actually injected into your component.

Dispatch only injects components in the following cases:

  1. You do not provide **mapDispatchToProps**

The default mapDispatchToProps is simply dispatch => ({dispatch}). If you do not provide mapDispatchToProps, dispatch will be provided to you automatically in the form above.

In other words, you did this:

// Connect (mapStateToProps /** No second argument */)(Component);Copy the code
  1. Your custom mapDispatchToProps explicitly returns dispatch**

You might want to bring the dispatch back to your component by defining it as follows:

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

This error can be referenced to Redux’s GitHub Issue #255.

Discussion about whether you need to provide dispatches for components (Dan Abramov’s reply to # 255). You can read them to learn more about the current purpose of doing this.

Can I just use mapDispatchToProps instead of mapStateToProps?

Of course. You can skip it by passing null or undefined to the first argument. Your component will not subscribe to store but will still receive dispatch props defined by mapDispatchToProps

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

Can I call store.dispatch?

This is not a good way to interact with a store, either directly imported or retrieved from the context (see Redux FAQ Entry on Store Setup for more details). Let React-Redux’s connect get access to the Store and dispatch actions.

Links and References

The tutorial

  • You Might Not Need the mapDispatchToProps Function

The related documents

  • Redux Doc on bindActionCreators

Q&A

  • How to get simple dispatch from this.props using connect with Redux?

  • this.props.dispatch is undefined if using mapDispatchToProps

  • Do not call store.dispatch, call this.props.dispatch injected by connect instead

  • Can I mapDispatchToProps without mapStateToProps in Redux?

  • Redux Doc FAQ: React Redux