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
-
state
-
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
-
dispatch
-
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:
-
A function (Action Creator) or an object (one Action Creator for each property)
-
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:
-
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
-
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