Connect: Extracting Data with mapStateToProps

As the first argument passed in to connect, mapStateToProps is used for selecting the part of the data from the store that the connected component needs. It’s frequently referred to as just mapState for short.

  • It is called every time the store state changes.

  • It receives the entire store state, and should return an object of data this component needs.

Defining mapStateToProps

mapStateToProps should be defined as a function:

function mapStateToProps(state, ownProps?)

It should take a first argument called state, optionally a second argument called ownProps, and return a plain object containing the data that the connected component needs.

This function should be passed as the first argument to connect, and will be called every time when the Redux store state changes. If you do not wish to subscribe to the store, pass null or undefined to connect in place of mapStateToProps.

It does not matter if a mapStateToProps function is written using the function keyword (function mapState(state) { } ) or as an arrow function (const mapState = (state) => { } ) - it will work the same either way.

Arguments

  1. state

  2. ownProps (optional)

state

The first argument to a mapStateToProps function is the entire Redux store state (the same value returned by a call to store.getState()). Because of this, the first argument is traditionally just called state. (While you can give the argument any name you want, calling it store would be incorrect - it's the "state value", not the "store instance".)

The mapStateToProps function should always be written with at least state passed in.

// TodoList.js

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

export default connect(mapStateToProps)(TodoList)

ownProps (optional)

You may define the function with a second argument, ownProps, if your component needs the data from its own props to retrieve data from the store. This argument will contain all of the props given to the wrapper component that was generated by connect.

// Todo.js

function mapStateToProps(state, ownProps) {
  const { visibilityFilter } = state
  const { id } = ownProps
  const todo = getTodoById(state, id)

  // component receives additionally:
  return { todo, visibilityFilter }
}

// Later, in your application, a parent component renders:
;<ConnectedTodo id={123} />
// and your component receives props.id, props.todo, and props.visibilityFilter

You do not need to include values from ownProps in the object returned from mapStateToProps. connect will automatically merge those different prop sources into a final set of props.

Return

Your mapStateToProps function should return a plain object that contains the data the component needs:

  • Each field in the object will become a prop for your actual component

  • The values in the fields will be used to determine if your component needs to re-render

For example:

function mapStateToProps(state) {
  return {
    a: 42,
    todos: state.todos,
    filter: state.visibilityFilter,
  }
}

// component will receive: props.a, props.todos, and props.filter

Note: In advanced scenarios where you need more control over the rendering performance, mapStateToProps can also return a function. In this case, that function will be used as the final mapStateToProps for a particular component instance. This allows you to do per-instance memoization. See the Advanced Usage section of the docs for more details, as well as PR #279 and the tests it adds. Most apps never need this.

Usage Guidelines

Let mapStateToProps Reshape the Data from the Store

mapStateToProps functions can, and should, do a lot more than just return state.someSlice. They have the responsibility of "re-shaping" store data as needed for that component. This may include returning a value as a specific prop name, combining pieces of data from different parts of the state tree, and transforming the store data in different ways.

Use Selector Functions to Extract and Transform Data

We highly encourage the use of "selector" functions to help encapsulate the process of extracting values from specific locations in the state tree. Memoized selector functions also play a key role in improving application performance (see the following sections in this page and the Advanced Usage: Performance page for more details on why and how to use selectors.)

mapStateToProps Functions Should Be Fast

Whenever the store changes, all of the mapStateToProps functions of all of the connected components will run. Because of this, your mapStateToProps functions should run as fast as possible. This also means that a slow mapStateToProps function can be a potential bottleneck for your application.

As part of the "re-shaping data" idea, mapStateToProps functions frequently need to transform data in various ways (such as filtering an array, mapping an array of IDs to their corresponding objects, or extracting plain JS values from Immutable.js objects). These transformations can often be expensive, both in terms of cost to execute the transformation, and whether the component re-renders as a result. If performance is a concern, ensure that these transformations are only run if the input values have changed.

mapStateToProps Functions Should Be Pure and Synchronous

Much like a Redux reducer, a mapStateToProps function should always be 100% pure and synchronous. It should simply take state (and ownProps) as arguments, and return the data the component needs as props. It should not be used to trigger asynchronous behavior like AJAX calls for data fetching, and the functions should not be declared as async.

mapStateToProps and Performance

Return Values Determine If Your Component Re-Renders

React-Redux internally implements the shouldComponentUpdate method such that the wrapper component re-renders precisely when the data your component needs has changed. By default, React-Redux decides whether the contents of the object returned from mapStateToProps are different using === comparison (a "shallow equality" check) on each fields of the returned object. If any of the fields have changed, then your component will be re-rendered so it can receive the updated values as props. Note that returning a mutated object of the same reference is a common mistake that can result in your component not re-rendering when expected.

To summarize the behavior of the component wrapped by connect with mapStateToProps to extract data from the store:

(state) => stateProps

(state, ownProps) => stateProps

mapStateToProps runs when:

store state changes

store state changes or any field of ownProps is different

component re-renders when:

any field of stateProps is different

any field of stateProps is different or any field of ownProps is different

Only Return New Object References If Needed

React-Redux does shallow comparisons to see if the mapStateToProps results have changed. It’s easy to accidentally return new object or array references every time, which would cause your component to re-render even if the data is actually the same.

Many common operations result in new object or array references being created:

  • Creating new arrays with someArray.map() or someArray.filter()

  • Merging arrays with array.concat

  • Selecting portion of an array with array.slice

  • Copying values with Object.assign

  • Copying values with the spread operator { ...oldState, ...newData }

Put these operations in memoized selector functions to ensure that they only run if the input values have changed. This will also ensure that if the input values haven't changed, mapStateToProps will still return the same result values as before, and connect can skip re-rendering.

Only Perform Expensive Operations When Data Changes

Transforming data can often be expensive (and usually results in new object references being created). In order for your mapStateToProps function to be as fast as possible, you should only re-run these complex transformations when the relevant data has changed.

There are a few ways to approach this:

  • Some transformations could be calculated in an action creator or reducer, and the transformed data could be kept in the store

  • Transformations can also be done in a component's render() method

  • If the transformation does need to be done in a mapStateToProps function, then we recommend using memoized selector functions to ensure the transformation is only run when the input values have changed.

Immutable.js Performance Concerns

Immutable.js author Lee Byron on Twitter explicitly advises avoiding toJS when performance is a concern:

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

There's several other performance concerns to take into consideration with Immutable.js - see the list of links at the end of this page for more information.

Behavior and Gotchas

mapStateToProps Will Not Run if the Store State is the Same

The wrapper component generated by connect subscribes to the Redux store. Every time an action is dispatched, it calls store.getState() and checks to see if lastState === currentState. If the two state values are identical by reference, then it will not re-run your mapStateToProps function, because it assumes that the rest of the store state hasn't changed either.

The Redux combineReducers utility function tries to optimize for this. If none of the slice reducers returned a new value, then combineReducers returns the old state object instead of a new one. This means that mutation in a reducer can lead to the root state object not being updated, and thus the UI won't re-render.

The Number of Declared Arguments Affects Behavior

With just (state), the function runs whenever the root store state object is different. With (state, ownProps), it runs any time the store state is different and ALSO whenever the wrapper props have changed.

This means that you should not add the ownProps argument unless you actually need to use it, or your mapStateToProps function will run more often than it needs to.

There are some edge cases around this behavior. The number of mandatory arguments determines whether mapStateToProps will receive ownProps.

If the formal definition of the function contains one mandatory parameter, mapStateToProps will not receive 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
}

It will receive ownProps when the formal definition of the function contains zero or two mandatory parameters:

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
}

Tutorials

Performance

Q&A

Last updated