From 8afa8617f99eedec9424c73fae14f8367ce27127 Mon Sep 17 00:00:00 2001 From: Wei Gao Date: Mon, 8 Oct 2018 17:30:28 +0800 Subject: [PATCH 1/4] Move mapState into Using React-Redux directory. Doc fixes: Fix typo. Remove comments as they get picked up by doc site generation. --- docs/README.md | 2 +- docs/getting-started.md | 3 +- ...ct-extracting-data-with-mapStateToProps.md | 224 ++++++++++++++++++ 3 files changed, 226 insertions(+), 3 deletions(-) create mode 100644 docs/using-react-redux/connect-extracting-data-with-mapStateToProps.md diff --git a/docs/README.md b/docs/README.md index 8ccb64c27..fd1256e3c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,7 +2,7 @@ - [Getting Started: adding React-Redux to a React todo app](./getting-started.md) - Using React-Redux - - [Connect: Extracting Data with `mapStateToProps`](./connect-extracting-data-with-mapStateToProps.md) + - [Connect: Extracting Data with `mapStateToProps`](./using-react-redux/connect-extracting-data-with-mapStateToProps.md) - [API](api.md#api) - [``](api.md#provider-store) - [`connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])`](api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options) diff --git a/docs/getting-started.md b/docs/getting-started.md index f56c2e08c..fce40e6dd 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -187,7 +187,7 @@ Our `addTodo` action creator looks like this: ```js // redux/actions.js -import { ADD_TODO } from './actionTypes'; +import { ADD_TODO } from "./actionTypes"; let nextTodoId = 0; export const addTodo = content => ({ @@ -497,7 +497,6 @@ Now we've finished a very simple example of a todo app with React-Redux. All our - [Usage with React](https://redux.js.org/basics/usagewithreact) - [Using the React-Redux Bindings](https://blog.isquaredsoftware.com/presentations/workshops/redux-fundamentals/react-redux.html) - [Higher Order Components in Depth](https://medium.com/@franleplant/react-higher-order-components-in-depth-cf9032ee6c3e) - - [Computing Derived Data](https://redux.js.org/recipes/computingderiveddata#sharing-selectors-across-multiple-components) - [Idiomatic Redux: Using Reselect Selectors for Encapsulation and Performance](https://blog.isquaredsoftware.com/2017/12/idiomatic-redux-using-reselect-selectors/) diff --git a/docs/using-react-redux/connect-extracting-data-with-mapStateToProps.md b/docs/using-react-redux/connect-extracting-data-with-mapStateToProps.md new file mode 100644 index 000000000..3aee5d05a --- /dev/null +++ b/docs/using-react-redux/connect-extracting-data-with-mapStateToProps.md @@ -0,0 +1,224 @@ +--- +id: connect-extracting-data-with-mapStateToProps +title: Connect: Extracting Data with mapStateToProps +hide_title: true +sidebar_label: Connect: Extracting Data with mapStateToProps +--- + +# 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: + +```js +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 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 + +#### `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. + +```js +// 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`. + +```js +// 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: +; +// 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: + +```js +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](https://github.com/reduxjs/react-redux/pull/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 `mapState` 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` +- 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, `mapState` 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](https://twitter.com/leeb/status/746733697093668864?lang=en): + +> 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`: + +```js +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: + +```js +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 +} +``` + +## Links and References + +**Tutorials** + +- [Practical Redux Series, Part 6: Connected Lists, Forms, and Performance](https://blog.isquaredsoftware.com/2017/01/practical-redux-part-6-connected-lists-forms-and-performance/) +- [Idiomatic Redux: Using Reselect Selectors for Encapsulation and Performance](https://blog.isquaredsoftware.com/2017/12/idiomatic-redux-using-reselect-selectors/) + +**Performance** + +- [Lee Byron's Tweet Suggesting to avoid `toJS`, `toArray` and `toObject` for Performance](https://twitter.com/leeb/status/746733697093668864) +- [Improving React and Redux performance with Reselect](https://blog.rangle.io/react-and-redux-performance-with-reselect/) +- [Immutable data performance links](https://github.com/markerikson/react-redux-links/blob/master/react-performance.md#immutable-data) + +**Q&A** + +- [Why Is My Component Re-Rendering Too Often?](https://redux.js.org/faq/reactredux#why-is-my-component-re-rendering-too-often) +- [Why isn't my component re-rendering, or my mapStateToProps running](https://redux.js.org/faq/reactredux#why-isnt-my-component-re-rendering-or-my-mapstatetoprops-running) +- [How can I speed up my mapStateToProps?](https://redux.js.org/faq/reactredux#why-is-my-component-re-rendering-too-often) +- [Should I only connect my top component, or can I connect multiple components in my tree?](https://redux.js.org/faq/reactredux#why-is-my-component-re-rendering-too-often) From fb9a40c9b16007f3b7e32ccd869dc9d9986f344f Mon Sep 17 00:00:00 2001 From: Wei Gao Date: Mon, 8 Oct 2018 17:31:13 +0800 Subject: [PATCH 2/4] Doc site header, footer, and sidebar changes. --- docs/api.md | 295 ++++++++++-------- ...ct-extracting-data-with-mapStateToProps.md | 254 --------------- website/core/Footer.js | 17 +- website/sidebars.json | 4 +- website/siteConfig.js | 13 +- 5 files changed, 189 insertions(+), 394 deletions(-) delete mode 100644 docs/connect-extracting-data-with-mapStateToProps.md diff --git a/docs/api.md b/docs/api.md index 78b2eb2db..3c85865c1 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,23 +1,24 @@ --- id: api title: Api -sidebar_label: Api +sidebar_label: API hide_title: true --- # API + ## Provider Makes the Redux store available to the `connect()` calls in the component hierarchy below. Normally, you can’t use `connect()` without wrapping a parent or ancestor component in ``. -If you *really* need to, you can manually pass `store` as a prop to every `connect()`ed component, but we only recommend to do this for stubbing `store` in unit tests, or in non-fully-React codebases. Normally, you should just use ``. +If you _really_ need to, you can manually pass `store` as a prop to every `connect()`ed component, but we only recommend to do this for stubbing `store` in unit tests, or in non-fully-React codebases. Normally, you should just use ``. ### Props -* `store` (*[Redux Store](https://redux.js.org/api-reference/store)*): The single Redux store in your application. -* `children` (*ReactElement*) The root of your component hierarchy. +- `store` (_[Redux Store](https://redux.js.org/api-reference/store)_): The single Redux store in your application. +- `children` (_ReactElement_) The root of your component hierarchy. ### Example @@ -29,7 +30,7 @@ ReactDOM.render( , rootEl -) +); ``` #### React Router @@ -39,16 +40,15 @@ ReactDOM.render( - - + + , - document.getElementById('root') -) + document.getElementById("root") +); ``` - ## connect ``` @@ -57,37 +57,37 @@ connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options]) Connects a React component to a Redux store. `connect` is a facade around `connectAdvanced`, providing a convenient API for the most common use cases. -It does not modify the component class passed to it; instead, it *returns* a new, connected component class for you to use. +It does not modify the component class passed to it; instead, it _returns_ a new, connected component class for you to use. ### Arguments -* [`mapStateToProps(state, [ownProps]): stateProps`] \(*Function*): If this argument is specified, the new component will subscribe to Redux store updates. This means that any time the store is updated, `mapStateToProps` will be called. The results of `mapStateToProps` must be a plain object, which will be merged into the component’s props. If you don't want to subscribe to store updates, pass `null` or `undefined` in place of `mapStateToProps`. +- [`mapStateToProps(state, [ownProps]): stateProps`] \(_Function_): If this argument is specified, the new component will subscribe to Redux store updates. This means that any time the store is updated, `mapStateToProps` will be called. The results of `mapStateToProps` must be a plain object, which will be merged into the component’s props. If you don't want to subscribe to store updates, pass `null` or `undefined` in place of `mapStateToProps`. - If your `mapStateToProps` function is declared as taking two parameters, it will be called with the store state as the first parameter and the props passed to the connected component as the second parameter, and will also be re-invoked whenever the connected component receives new props as determined by shallow equality comparisons. (The second parameter is normally referred to as `ownProps` by convention.) + If your `mapStateToProps` function is declared as taking two parameters, it will be called with the store state as the first parameter and the props passed to the connected component as the second parameter, and will also be re-invoked whenever the connected component receives new props as determined by shallow equality comparisons. (The second parameter is normally referred to as `ownProps` by convention.) - >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 `mapStateToProps()` for a particular component instance. This allows you to do per-instance memoization. You can refer to [#279](https://github.com/reduxjs/react-redux/pull/279) and the tests it adds for more details. Most apps never need this. + > 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 `mapStateToProps()` for a particular component instance. This allows you to do per-instance memoization. You can refer to [#279](https://github.com/reduxjs/react-redux/pull/279) and the tests it adds for more details. Most apps never need this. - >The `mapStateToProps` function's first argument is the entire Redux store’s state and it returns an object to be passed as props. It is often called a **selector**. Use [reselect](https://github.com/reduxjs/reselect) to efficiently compose selectors and [compute derived data](https://redux.js.org/recipes/computing-derived-data). + > The `mapStateToProps` function's first argument is the entire Redux store’s state and it returns an object to be passed as props. It is often called a **selector**. Use [reselect](https://github.com/reduxjs/reselect) to efficiently compose selectors and [compute derived data](https://redux.js.org/recipes/computing-derived-data). -* [`mapDispatchToProps(dispatch, [ownProps]): dispatchProps`] \(*Object* or *Function*): If an object is passed, each function inside it is assumed to be a Redux action creator. An object with the same function names, but with every action creator wrapped into a `dispatch` call so they may be invoked directly, will be merged into the component’s props. +- [`mapDispatchToProps(dispatch, [ownProps]): dispatchProps`] \(_Object_ or _Function_): If an object is passed, each function inside it is assumed to be a Redux action creator. An object with the same function names, but with every action creator wrapped into a `dispatch` call so they may be invoked directly, will be merged into the component’s props. - If a function is passed, it will be given `dispatch` as the first parameter. It’s up to you to return an object that somehow uses `dispatch` to bind action creators in your own way. (Tip: you may use the [`bindActionCreators()`](https://redux.js.org/api-reference/bindactioncreators) helper from Redux.) + If a function is passed, it will be given `dispatch` as the first parameter. It’s up to you to return an object that somehow uses `dispatch` to bind action creators in your own way. (Tip: you may use the [`bindActionCreators()`](https://redux.js.org/api-reference/bindactioncreators) helper from Redux.) - If your `mapDispatchToProps` function is declared as taking two parameters, it will be called with `dispatch` as the first parameter and the props passed to the connected component as the second parameter, and will be re-invoked whenever the connected component receives new props. (The second parameter is normally referred to as `ownProps` by convention.) + If your `mapDispatchToProps` function is declared as taking two parameters, it will be called with `dispatch` as the first parameter and the props passed to the connected component as the second parameter, and will be re-invoked whenever the connected component receives new props. (The second parameter is normally referred to as `ownProps` by convention.) If you do not supply your own `mapDispatchToProps` function or object full of action creators, the default `mapDispatchToProps` implementation just injects `dispatch` into your component’s props. - >Note: in advanced scenarios where you need more control over the rendering performance, `mapDispatchToProps()` can also return a function. In this case, *that* function will be used as `mapDispatchToProps()` for a particular component instance. This allows you to do per-instance memoization. You can refer to [#279](https://github.com/reduxjs/react-redux/pull/279) and the tests it adds for more details. Most apps never need this. + > Note: in advanced scenarios where you need more control over the rendering performance, `mapDispatchToProps()` can also return a function. In this case, _that_ function will be used as `mapDispatchToProps()` for a particular component instance. This allows you to do per-instance memoization. You can refer to [#279](https://github.com/reduxjs/react-redux/pull/279) and the tests it adds for more details. Most apps never need this. -* [`mergeProps(stateProps, dispatchProps, ownProps): props`] \(*Function*): If specified, it is passed the result of `mapStateToProps()`, `mapDispatchToProps()`, and the parent `props`. The plain object you return from it will be passed as props to the wrapped component. You may specify this function to select a slice of the state based on props, or to bind action creators to a particular variable from props. If you omit it, `Object.assign({}, ownProps, stateProps, dispatchProps)` is used by default. +- [`mergeProps(stateProps, dispatchProps, ownProps): props`] \(_Function_): If specified, it is passed the result of `mapStateToProps()`, `mapDispatchToProps()`, and the parent `props`. The plain object you return from it will be passed as props to the wrapped component. You may specify this function to select a slice of the state based on props, or to bind action creators to a particular variable from props. If you omit it, `Object.assign({}, ownProps, stateProps, dispatchProps)` is used by default. -* [`options`] *(Object)* If specified, further customizes the behavior of the connector. In addition to the options passable to `connectAdvanced()` (see those below), `connect()` accepts these additional options: - * [`pure`] *(Boolean)*: If true, `connect()` will avoid re-renders and calls to `mapStateToProps`, `mapDispatchToProps`, and `mergeProps` if the relevant state/props objects remain equal based on their respective equality checks. Assumes that the wrapped component is a “pure” component and does not rely on any input or state other than its props and the selected Redux store’s state. Default value: `true` - * [`areStatesEqual`] *(Function)*: When pure, compares incoming store state to its previous value. Default value: `strictEqual (===)` - * [`areOwnPropsEqual`] *(Function)*: When pure, compares incoming props to its previous value. Default value: `shallowEqual` - * [`areStatePropsEqual`] *(Function)*: When pure, compares the result of `mapStateToProps` to its previous value. Default value: `shallowEqual` - * [`areMergedPropsEqual`] *(Function)*: When pure, compares the result of `mergeProps` to its previous value. Default value: `shallowEqual` - * [`storeKey`] *(String)*: The key of the context from where to read the store. You probably only need this if you are in the inadvisable position of having multiple stores. Default value: `'store'` +- [`options`] _(Object)_ If specified, further customizes the behavior of the connector. In addition to the options passable to `connectAdvanced()` (see those below), `connect()` accepts these additional options: + - [`pure`] _(Boolean)_: If true, `connect()` will avoid re-renders and calls to `mapStateToProps`, `mapDispatchToProps`, and `mergeProps` if the relevant state/props objects remain equal based on their respective equality checks. Assumes that the wrapped component is a “pure” component and does not rely on any input or state other than its props and the selected Redux store’s state. Default value: `true` + - [`areStatesEqual`] _(Function)_: When pure, compares incoming store state to its previous value. Default value: `strictEqual (===)` + - [`areOwnPropsEqual`] _(Function)_: When pure, compares incoming props to its previous value. Default value: `shallowEqual` + - [`areStatePropsEqual`] _(Function)_: When pure, compares the result of `mapStateToProps` to its previous value. Default value: `shallowEqual` + - [`areMergedPropsEqual`] _(Function)_: When pure, compares the result of `mergeProps` to its previous value. Default value: `shallowEqual` + - [`storeKey`] _(String)_: The key of the context from where to read the store. You probably only need this if you are in the inadvisable position of having multiple stores. Default value: `'store'` #### The arity of mapStateToProps and mapDispatchToProps determines whether they receive ownProps @@ -99,238 +99,278 @@ function mapStateToProps(state) { console.log(arguments[1]); // undefined } ``` + ```js const mapStateToProps = (state, ownProps = {}) => { console.log(state); // state console.log(ownProps); // undefined -} +}; ``` + Functions with no mandatory parameters or two parameters **will receive** `ownProps`. + ```js const mapStateToProps = (state, ownProps) => { console.log(state); // state console.log(ownProps); // ownProps -} +}; ``` + ```js function mapStateToProps() { console.log(arguments[0]); // state console.log(arguments[1]); // ownProps } ``` + ```js const mapStateToProps = (...args) => { console.log(args[0]); // state console.log(args[1]); // ownProps -} +}; ``` #### Optimizing connect when options.pure is true When `options.pure` is true, `connect` performs several equality checks that are used to avoid unnecessary calls to `mapStateToProps`, `mapDispatchToProps`, `mergeProps`, and ultimately to `render`. These include `areStatesEqual`, `areOwnPropsEqual`, `areStatePropsEqual`, and `areMergedPropsEqual`. While the defaults are probably appropriate 99% of the time, you may wish to override them with custom implementations for performance or other reasons. Here are several examples: -* You may wish to override `areStatesEqual` if your `mapStateToProps` function is computationally expensive and is also only concerned with a small slice of your state. For example: `areStatesEqual: (next, prev) => prev.entities.todos === next.entities.todos`; this would effectively ignore state changes for everything but that slice of state. +- You may wish to override `areStatesEqual` if your `mapStateToProps` function is computationally expensive and is also only concerned with a small slice of your state. For example: `areStatesEqual: (next, prev) => prev.entities.todos === next.entities.todos`; this would effectively ignore state changes for everything but that slice of state. -* You may wish to override `areStatesEqual` to always return false (`areStatesEqual: () => false`) if you have impure reducers that mutate your store state. (This would likely impact the other equality checks as well, depending on your `mapStateToProps` function.) +- You may wish to override `areStatesEqual` to always return false (`areStatesEqual: () => false`) if you have impure reducers that mutate your store state. (This would likely impact the other equality checks as well, depending on your `mapStateToProps` function.) -* You may wish to override `areOwnPropsEqual` as a way to whitelist incoming props. You'd also have to implement `mapStateToProps`, `mapDispatchToProps` and `mergeProps` to also whitelist props. (It may be simpler to achieve this other ways, for example by using [recompose's mapProps](https://github.com/acdlite/recompose/blob/master/docs/API.md#mapprops).) +- You may wish to override `areOwnPropsEqual` as a way to whitelist incoming props. You'd also have to implement `mapStateToProps`, `mapDispatchToProps` and `mergeProps` to also whitelist props. (It may be simpler to achieve this other ways, for example by using [recompose's mapProps](https://github.com/acdlite/recompose/blob/master/docs/API.md#mapprops).) -* You may wish to override `areStatePropsEqual` to use `strictEqual` if your `mapStateToProps` uses a memoized selector that will only return a new object if a relevant prop has changed. This would be a very slight performance improvement, since would avoid extra equality checks on individual props each time `mapStateToProps` is called. +- You may wish to override `areStatePropsEqual` to use `strictEqual` if your `mapStateToProps` uses a memoized selector that will only return a new object if a relevant prop has changed. This would be a very slight performance improvement, since would avoid extra equality checks on individual props each time `mapStateToProps` is called. -* You may wish to override `areMergedPropsEqual` to implement a `deepEqual` if your selectors produce complex props. ex: nested objects, new arrays, etc. (The deep equal check should be faster than just re-rendering.) +- You may wish to override `areMergedPropsEqual` to implement a `deepEqual` if your selectors produce complex props. ex: nested objects, new arrays, etc. (The deep equal check should be faster than just re-rendering.) ### Returns A higher-order React component class that passes state and action creators into your component derived from the supplied arguments. This is created by `connectAdvanced`, and details of this higher-order component are covered there. + #### Examples #### Inject just `dispatch` and don't listen to store ```js -export default connect()(TodoApp) +export default connect()(TodoApp); ``` -#### Inject all action creators (`addTodo`, `completeTodo`, ...) without subscribing to the store +#### Inject all action creators (`addTodo`, `completeTodo`, ...) without subscribing to the store ```js -import * as actionCreators from './actionCreators' +import * as actionCreators from "./actionCreators"; -export default connect(null, actionCreators)(TodoApp) +export default connect( + null, + actionCreators +)(TodoApp); ``` #### Inject `dispatch` and every field in the global state ->Don’t do this! It kills any performance optimizations because `TodoApp` will rerender after every state change. ->It’s better to have more granular `connect()` on several components in your view hierarchy that each only ->listen to a relevant slice of the state. +> Don’t do this! It kills any performance optimizations because `TodoApp` will rerender after every state change. +> It’s better to have more granular `connect()` on several components in your view hierarchy that each only +> listen to a relevant slice of the state. ```js -export default connect(state => state)(TodoApp) +export default connect(state => state)(TodoApp); ``` #### Inject `dispatch` and `todos` ```js function mapStateToProps(state) { - return { todos: state.todos } + return { todos: state.todos }; } -export default connect(mapStateToProps)(TodoApp) +export default connect(mapStateToProps)(TodoApp); ``` #### Inject `todos` and all action creators ```js -import * as actionCreators from './actionCreators' +import * as actionCreators from "./actionCreators"; function mapStateToProps(state) { - return { todos: state.todos } + return { todos: state.todos }; } -export default connect(mapStateToProps, actionCreators)(TodoApp) +export default connect( + mapStateToProps, + actionCreators +)(TodoApp); ``` #### Inject `todos` and all action creators (`addTodo`, `completeTodo`, ...) as `actions` ```js -import * as actionCreators from './actionCreators' -import { bindActionCreators } from 'redux' +import * as actionCreators from "./actionCreators"; +import { bindActionCreators } from "redux"; function mapStateToProps(state) { - return { todos: state.todos } + return { todos: state.todos }; } function mapDispatchToProps(dispatch) { - return { actions: bindActionCreators(actionCreators, dispatch) } + return { actions: bindActionCreators(actionCreators, dispatch) }; } -export default connect(mapStateToProps, mapDispatchToProps)(TodoApp) +export default connect( + mapStateToProps, + mapDispatchToProps +)(TodoApp); ``` -#### Inject `todos` and a specific action creator (`addTodo`) +#### Inject `todos` and a specific action creator (`addTodo`) ```js -import { addTodo } from './actionCreators' -import { bindActionCreators } from 'redux' +import { addTodo } from "./actionCreators"; +import { bindActionCreators } from "redux"; function mapStateToProps(state) { - return { todos: state.todos } + return { todos: state.todos }; } function mapDispatchToProps(dispatch) { - return bindActionCreators({ addTodo }, dispatch) + return bindActionCreators({ addTodo }, dispatch); } -export default connect(mapStateToProps, mapDispatchToProps)(TodoApp) +export default connect( + mapStateToProps, + mapDispatchToProps +)(TodoApp); ``` -#### Inject `todos` and specific action creators (`addTodo` and `deleteTodo`) with shorthand syntax +#### Inject `todos` and specific action creators (`addTodo` and `deleteTodo`) with shorthand syntax ```js -import { addTodo, deleteTodo } from './actionCreators' +import { addTodo, deleteTodo } from "./actionCreators"; function mapStateToProps(state) { - return { todos: state.todos } + return { todos: state.todos }; } const mapDispatchToProps = { addTodo, deleteTodo -} +}; -export default connect(mapStateToProps, mapDispatchToProps)(TodoApp) +export default connect( + mapStateToProps, + mapDispatchToProps +)(TodoApp); ``` #### Inject `todos`, todoActionCreators as `todoActions`, and counterActionCreators as `counterActions` ```js -import * as todoActionCreators from './todoActionCreators' -import * as counterActionCreators from './counterActionCreators' -import { bindActionCreators } from 'redux' +import * as todoActionCreators from "./todoActionCreators"; +import * as counterActionCreators from "./counterActionCreators"; +import { bindActionCreators } from "redux"; function mapStateToProps(state) { - return { todos: state.todos } + return { todos: state.todos }; } function mapDispatchToProps(dispatch) { return { todoActions: bindActionCreators(todoActionCreators, dispatch), counterActions: bindActionCreators(counterActionCreators, dispatch) - } + }; } -export default connect(mapStateToProps, mapDispatchToProps)(TodoApp) +export default connect( + mapStateToProps, + mapDispatchToProps +)(TodoApp); ``` #### Inject `todos`, and todoActionCreators and counterActionCreators together as `actions` ```js -import * as todoActionCreators from './todoActionCreators' -import * as counterActionCreators from './counterActionCreators' -import { bindActionCreators } from 'redux' +import * as todoActionCreators from "./todoActionCreators"; +import * as counterActionCreators from "./counterActionCreators"; +import { bindActionCreators } from "redux"; function mapStateToProps(state) { - return { todos: state.todos } + return { todos: state.todos }; } function mapDispatchToProps(dispatch) { return { - actions: bindActionCreators(Object.assign({}, todoActionCreators, counterActionCreators), dispatch) - } + actions: bindActionCreators( + Object.assign({}, todoActionCreators, counterActionCreators), + dispatch + ) + }; } -export default connect(mapStateToProps, mapDispatchToProps)(TodoApp) +export default connect( + mapStateToProps, + mapDispatchToProps +)(TodoApp); ``` #### Inject `todos`, and all todoActionCreators and counterActionCreators directly as props ```js -import * as todoActionCreators from './todoActionCreators' -import * as counterActionCreators from './counterActionCreators' -import { bindActionCreators } from 'redux' +import * as todoActionCreators from "./todoActionCreators"; +import * as counterActionCreators from "./counterActionCreators"; +import { bindActionCreators } from "redux"; function mapStateToProps(state) { - return { todos: state.todos } + return { todos: state.todos }; } function mapDispatchToProps(dispatch) { - return bindActionCreators(Object.assign({}, todoActionCreators, counterActionCreators), dispatch) + return bindActionCreators( + Object.assign({}, todoActionCreators, counterActionCreators), + dispatch + ); } -export default connect(mapStateToProps, mapDispatchToProps)(TodoApp) +export default connect( + mapStateToProps, + mapDispatchToProps +)(TodoApp); ``` #### Inject `todos` of a specific user depending on props ```js -import * as actionCreators from './actionCreators' +import * as actionCreators from "./actionCreators"; function mapStateToProps(state, ownProps) { - return { todos: state.todos[ownProps.userId] } + return { todos: state.todos[ownProps.userId] }; } -export default connect(mapStateToProps)(TodoApp) +export default connect(mapStateToProps)(TodoApp); ``` #### Inject `todos` of a specific user depending on props, and inject `props.userId` into the action ```js -import * as actionCreators from './actionCreators' +import * as actionCreators from "./actionCreators"; function mapStateToProps(state) { - return { todos: state.todos } + return { todos: state.todos }; } function mergeProps(stateProps, dispatchProps, ownProps) { return Object.assign({}, ownProps, { todos: stateProps.todos[ownProps.userId], - addTodo: (text) => dispatchProps.addTodo(ownProps.userId, text) - }) + addTodo: text => dispatchProps.addTodo(ownProps.userId, text) + }); } -export default connect(mapStateToProps, actionCreators, mergeProps)(TodoApp) +export default connect( + mapStateToProps, + actionCreators, + mergeProps +)(TodoApp); ``` #### Factory functions @@ -370,32 +410,32 @@ export default connect(mapStateToPropsFactory, mapDispatchToPropsFactory)(TodoAp ## connectAdvanced ```js -connectAdvanced(selectorFactory, [connectOptions]) +connectAdvanced(selectorFactory, [connectOptions]); ``` Connects a React component to a Redux store. It is the base for `connect()` but is less opinionated about how to combine `state`, `props`, and `dispatch` into your final props. It makes no assumptions about defaults or memoization of results, leaving those responsibilities to the caller. -It does not modify the component class passed to it; instead, it *returns* a new, connected component class for you to use. +It does not modify the component class passed to it; instead, it _returns_ a new, connected component class for you to use. ### Arguments -* `selectorFactory(dispatch, factoryOptions): selector(state, ownProps): props` \(*Function*): Initializes a selector function (during each instance's constructor). That selector function is called any time the connector component needs to compute new props, as a result of a store state change or receiving new props. The result of `selector` is expected to be a plain object, which is passed as the props to the wrapped component. If a consecutive call to `selector` returns the same object (`===`) as its previous call, the component will not be re-rendered. It's the responsibility of `selector` to return that previous object when appropriate. +- `selectorFactory(dispatch, factoryOptions): selector(state, ownProps): props` \(_Function_): Initializes a selector function (during each instance's constructor). That selector function is called any time the connector component needs to compute new props, as a result of a store state change or receiving new props. The result of `selector` is expected to be a plain object, which is passed as the props to the wrapped component. If a consecutive call to `selector` returns the same object (`===`) as its previous call, the component will not be re-rendered. It's the responsibility of `selector` to return that previous object when appropriate. -* [`connectOptions`] *(Object)* If specified, further customizes the behavior of the connector. +- [`connectOptions`] _(Object)_ If specified, further customizes the behavior of the connector. - * [`getDisplayName`] *(Function)*: computes the connector component's displayName property relative to that of the wrapped component. Usually overridden by wrapper functions. Default value: `name => 'ConnectAdvanced('+name+')'` + - [`getDisplayName`] _(Function)_: computes the connector component's displayName property relative to that of the wrapped component. Usually overridden by wrapper functions. Default value: `name => 'ConnectAdvanced('+name+')'` - * [`methodName`] *(String)*: shown in error messages. Usually overridden by wrapper functions. Default value: `'connectAdvanced'` + - [`methodName`] _(String)_: shown in error messages. Usually overridden by wrapper functions. Default value: `'connectAdvanced'` - * [`renderCountProp`] *(String)*: if defined, a property named this value will be added to the props passed to the wrapped component. Its value will be the number of times the component has been rendered, which can be useful for tracking down unnecessary re-renders. Default value: `undefined` + - [`renderCountProp`] _(String)_: if defined, a property named this value will be added to the props passed to the wrapped component. Its value will be the number of times the component has been rendered, which can be useful for tracking down unnecessary re-renders. Default value: `undefined` - * [`shouldHandleStateChanges`] *(Boolean)*: controls whether the connector component subscribes to redux store state changes. If set to false, it will only re-render when parent component re-renders. Default value: `true` + - [`shouldHandleStateChanges`] _(Boolean)_: controls whether the connector component subscribes to redux store state changes. If set to false, it will only re-render when parent component re-renders. Default value: `true` - * [`storeKey`] *(String)*: the key of props/context to get the store. You probably only need this if you are in the inadvisable position of having multiple stores. Default value: `'store'` + - [`storeKey`] _(String)_: the key of props/context to get the store. You probably only need this if you are in the inadvisable position of having multiple stores. Default value: `'store'` - * [`withRef`] *(Boolean)*: If true, stores a ref to the wrapped component instance and makes it available via `getWrappedInstance()` method. Default value: `false` + - [`withRef`] _(Boolean)_: If true, stores a ref to the wrapped component instance and makes it available via `getWrappedInstance()` method. Default value: `false` - * Additionally, any extra options passed via `connectOptions` will be passed through to your `selectorFactory` in the `factoryOptions` argument. + - Additionally, any extra options passed via `connectOptions` will be passed through to your `selectorFactory` in the `factoryOptions` argument. @@ -405,7 +445,7 @@ A higher-order React component class that builds props from the store state and #### Static Properties -* `WrappedComponent` *(Component)*: The original component class passed to `connectAdvanced(...)(Component)`. +- `WrappedComponent` _(Component)_: The original component class passed to `connectAdvanced(...)(Component)`. #### Static Methods @@ -419,56 +459,58 @@ Returns the wrapped component instance. Only available if you pass `{ withRef: t ### Remarks -* Since `connectAdvanced` returns a higher-order component, it needs to be invoked two times. The first time with its arguments as described above, and a second time, with the component: `connectAdvanced(selectorFactory)(MyComponent)`. +- Since `connectAdvanced` returns a higher-order component, it needs to be invoked two times. The first time with its arguments as described above, and a second time, with the component: `connectAdvanced(selectorFactory)(MyComponent)`. -* `connectAdvanced` does not modify the passed React component. It returns a new, connected component, that you should use instead. +- `connectAdvanced` does not modify the passed React component. It returns a new, connected component, that you should use instead. + #### Examples #### Inject `todos` of a specific user depending on props, and inject `props.userId` into the action ```js -import * as actionCreators from './actionCreators' -import { bindActionCreators } from 'redux' +import * as actionCreators from "./actionCreators"; +import { bindActionCreators } from "redux"; function selectorFactory(dispatch) { - let ownProps = {} - let result = {} - const actions = bindActionCreators(actionCreators, dispatch) - const addTodo = (text) => actions.addTodo(ownProps.userId, text) + let ownProps = {}; + let result = {}; + const actions = bindActionCreators(actionCreators, dispatch); + const addTodo = text => actions.addTodo(ownProps.userId, text); return (nextState, nextOwnProps) => { - const todos = nextState.todos[nextOwnProps.userId] - const nextResult = { ...nextOwnProps, todos, addTodo } - ownProps = nextOwnProps - if (!shallowEqual(result, nextResult)) result = nextResult - return result - } + const todos = nextState.todos[nextOwnProps.userId]; + const nextResult = { ...nextOwnProps, todos, addTodo }; + ownProps = nextOwnProps; + if (!shallowEqual(result, nextResult)) result = nextResult; + return result; + }; } -export default connectAdvanced(selectorFactory)(TodoApp) +export default connectAdvanced(selectorFactory)(TodoApp); ``` ## createProvider ```js -createProvider([storeKey]) +createProvider([storeKey]); ``` Creates a new `` which will set the Redux Store on the passed key of the context. You probably only need this if you are in the inadvisable position of having multiple stores. You will also need to pass the same `storeKey` to the `options` argument of [`connect`](#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options) ### Arguments -* [`storeKey`] (*String*): The key of the context on which to set the store. Default value: `'store'` +- [`storeKey`](*String*): The key of the context on which to set the store. Default value: `'store'` ### Examples + Before creating multiple stores, please go through this FAQ: [Can or should I create multiple stores?](http://redux.js.org/docs/faq/StoreSetup.html#can-or-should-i-create-multiple-stores-can-i-import-my-store-directly-and-use-it-in-components-myself) ```js -import {connect, createProvider} from 'react-redux' +import { connect, createProvider } from "react-redux"; -const STORE_KEY = 'componentStore' +const STORE_KEY = "componentStore"; -export const Provider = createProvider(STORE_KEY) +export const Provider = createProvider(STORE_KEY); function connectExtended( mapStateToProps, @@ -476,15 +518,16 @@ function connectExtended( mergeProps, options = {} ) { - options.storeKey = STORE_KEY + options.storeKey = STORE_KEY; return connect( mapStateToProps, mapDispatchToProps, mergeProps, options - ) + ); } -export {connectExtended as connect} +export { connectExtended as connect }; ``` + Now you can import the above `Provider` and `connect` and use them. diff --git a/docs/connect-extracting-data-with-mapStateToProps.md b/docs/connect-extracting-data-with-mapStateToProps.md deleted file mode 100644 index 228d37666..000000000 --- a/docs/connect-extracting-data-with-mapStateToProps.md +++ /dev/null @@ -1,254 +0,0 @@ ---- -id: connect-extracting-data-with-mapStateToProps -title: Connect: Extracting Data with mapStateToProps -hide_title: true -sidebar_label: Connect: Extracting Data with mapStateToProps ---- - -# 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: - -```js -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 - -#### `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. - -```js -// 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`. - -```js -// 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: - -// 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: - -```js -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](https://github.com/reduxjs/react-redux/pull/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` is `===` different | 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 `mapState` 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` -- 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, `mapState` 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](https://twitter.com/leeb/status/746733697093668864?lang=en): - -> 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`: - -```js -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: - -```js -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 -} -``` - - - - - - -## Links and References - -**Tutorials** - -- [Practical Redux Series, Part 6: Connected Lists, Forms, and Performance](https://blog.isquaredsoftware.com/2017/01/practical-redux-part-6-connected-lists-forms-and-performance/) -- [Idiomatic Redux: Using Reselect Selectors for Encapsulation and Performance](https://blog.isquaredsoftware.com/2017/12/idiomatic-redux-using-reselect-selectors/) - -**Performance** - -- [Lee Byron's Tweet Suggesting to avoid `toJS`, `toArray` and `toObject` for Performance](https://twitter.com/leeb/status/746733697093668864) -- [Improving React and Redux performance with Reselect](https://blog.rangle.io/react-and-redux-performance-with-reselect/) -- [Immutable data performance links](https://github.com/markerikson/react-redux-links/blob/master/react-performance.md#immutable-data) - -**Q&A** - -- [Why Is My Component Re-Rendering Too Often?](https://redux.js.org/faq/reactredux#why-is-my-component-re-rendering-too-often) -- [Why isn't my component re-rendering, or my mapStateToProps running](https://redux.js.org/faq/reactredux#why-isnt-my-component-re-rendering-or-my-mapstatetoprops-running) -- [How can I speed up my mapStateToProps?](https://redux.js.org/faq/reactredux#why-is-my-component-re-rendering-too-often) -- [Should I only connect my top component, or can I connect multiple components in my tree?](https://redux.js.org/faq/reactredux#why-is-my-component-re-rendering-too-often) diff --git a/website/core/Footer.js b/website/core/Footer.js index 2d825d7ee..1102b74aa 100644 --- a/website/core/Footer.js +++ b/website/core/Footer.js @@ -34,15 +34,16 @@ class Footer extends React.Component {
Community
diff --git a/website/sidebars.json b/website/sidebars.json index af3f2346f..994b3dd38 100644 --- a/website/sidebars.json +++ b/website/sidebars.json @@ -1,7 +1,9 @@ { "docs": { "Introduction": ["getting-started"], - "Using React Redux": ["connect-extracting-data-with-mapStateToProps"], + "Using React-Redux": [ + "using-react-redux/connect-extracting-data-with-mapStateToProps" + ], "API Reference": ["api", "api/provider"], "Guides": ["troubleshooting"] } diff --git a/website/siteConfig.js b/website/siteConfig.js index 62ecf5f12..dca879725 100644 --- a/website/siteConfig.js +++ b/website/siteConfig.js @@ -26,9 +26,12 @@ const siteConfig = { // For no header links in the top nav bar -> headerLinks: [], headerLinks: [ - { doc: "getting-started", label: "Getting started" }, - { doc: "api", label: "Api" }, - { doc: "troubleshooting", label: "Troubleshooting" } + { doc: "getting-started", label: "Getting Started" }, + { + doc: "using-react-redux/connect-extracting-data-with-mapStateToProps", + label: "Using React-Redux" + }, + { doc: "api", label: "API" } ], /* path to images for header/footer */ @@ -39,7 +42,7 @@ const siteConfig = { /* Colors for website */ colors: { primaryColor: "#764ABC", - secondaryColor: "#764ABC", + secondaryColor: "#764ABC" }, /* Custom fonts for website */ @@ -78,7 +81,7 @@ const siteConfig = { // You may provide arbitrary config keys to be used as needed by your // template. For example, if you need your repo's URL... - repoUrl: "https://github.com/reduxjs/react-redux", + repoUrl: "https://github.com/reduxjs/react-redux" }; module.exports = siteConfig; From 43f416e094d69d4b9dd7906a4032fed48546d80d Mon Sep 17 00:00:00 2001 From: Wei Gao Date: Mon, 8 Oct 2018 23:32:59 +0800 Subject: [PATCH 3/4] Revert prettier result on api and mapstate docs Revert prettier changes on mapstate --- docs/api.md | 293 ++++++++---------- ...ct-extracting-data-with-mapStateToProps.md | 88 ++++-- 2 files changed, 179 insertions(+), 202 deletions(-) diff --git a/docs/api.md b/docs/api.md index 3c85865c1..3252f6e7f 100644 --- a/docs/api.md +++ b/docs/api.md @@ -8,17 +8,16 @@ hide_title: true # API - ## Provider Makes the Redux store available to the `connect()` calls in the component hierarchy below. Normally, you can’t use `connect()` without wrapping a parent or ancestor component in ``. -If you _really_ need to, you can manually pass `store` as a prop to every `connect()`ed component, but we only recommend to do this for stubbing `store` in unit tests, or in non-fully-React codebases. Normally, you should just use ``. +If you *really* need to, you can manually pass `store` as a prop to every `connect()`ed component, but we only recommend to do this for stubbing `store` in unit tests, or in non-fully-React codebases. Normally, you should just use ``. ### Props -- `store` (_[Redux Store](https://redux.js.org/api-reference/store)_): The single Redux store in your application. -- `children` (_ReactElement_) The root of your component hierarchy. +* `store` (*[Redux Store](https://redux.js.org/api-reference/store)*): The single Redux store in your application. +* `children` (*ReactElement*) The root of your component hierarchy. ### Example @@ -30,7 +29,7 @@ ReactDOM.render( , rootEl -); +) ``` #### React Router @@ -40,15 +39,16 @@ ReactDOM.render( - - + + , - document.getElementById("root") -); + document.getElementById('root') +) ``` + ## connect ``` @@ -57,37 +57,37 @@ connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options]) Connects a React component to a Redux store. `connect` is a facade around `connectAdvanced`, providing a convenient API for the most common use cases. -It does not modify the component class passed to it; instead, it _returns_ a new, connected component class for you to use. +It does not modify the component class passed to it; instead, it *returns* a new, connected component class for you to use. ### Arguments -- [`mapStateToProps(state, [ownProps]): stateProps`] \(_Function_): If this argument is specified, the new component will subscribe to Redux store updates. This means that any time the store is updated, `mapStateToProps` will be called. The results of `mapStateToProps` must be a plain object, which will be merged into the component’s props. If you don't want to subscribe to store updates, pass `null` or `undefined` in place of `mapStateToProps`. +* [`mapStateToProps(state, [ownProps]): stateProps`] \(*Function*): If this argument is specified, the new component will subscribe to Redux store updates. This means that any time the store is updated, `mapStateToProps` will be called. The results of `mapStateToProps` must be a plain object, which will be merged into the component’s props. If you don't want to subscribe to store updates, pass `null` or `undefined` in place of `mapStateToProps`. - If your `mapStateToProps` function is declared as taking two parameters, it will be called with the store state as the first parameter and the props passed to the connected component as the second parameter, and will also be re-invoked whenever the connected component receives new props as determined by shallow equality comparisons. (The second parameter is normally referred to as `ownProps` by convention.) + If your `mapStateToProps` function is declared as taking two parameters, it will be called with the store state as the first parameter and the props passed to the connected component as the second parameter, and will also be re-invoked whenever the connected component receives new props as determined by shallow equality comparisons. (The second parameter is normally referred to as `ownProps` by convention.) - > 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 `mapStateToProps()` for a particular component instance. This allows you to do per-instance memoization. You can refer to [#279](https://github.com/reduxjs/react-redux/pull/279) and the tests it adds for more details. Most apps never need this. + >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 `mapStateToProps()` for a particular component instance. This allows you to do per-instance memoization. You can refer to [#279](https://github.com/reduxjs/react-redux/pull/279) and the tests it adds for more details. Most apps never need this. - > The `mapStateToProps` function's first argument is the entire Redux store’s state and it returns an object to be passed as props. It is often called a **selector**. Use [reselect](https://github.com/reduxjs/reselect) to efficiently compose selectors and [compute derived data](https://redux.js.org/recipes/computing-derived-data). + >The `mapStateToProps` function's first argument is the entire Redux store’s state and it returns an object to be passed as props. It is often called a **selector**. Use [reselect](https://github.com/reduxjs/reselect) to efficiently compose selectors and [compute derived data](https://redux.js.org/recipes/computing-derived-data). -- [`mapDispatchToProps(dispatch, [ownProps]): dispatchProps`] \(_Object_ or _Function_): If an object is passed, each function inside it is assumed to be a Redux action creator. An object with the same function names, but with every action creator wrapped into a `dispatch` call so they may be invoked directly, will be merged into the component’s props. +* [`mapDispatchToProps(dispatch, [ownProps]): dispatchProps`] \(*Object* or *Function*): If an object is passed, each function inside it is assumed to be a Redux action creator. An object with the same function names, but with every action creator wrapped into a `dispatch` call so they may be invoked directly, will be merged into the component’s props. - If a function is passed, it will be given `dispatch` as the first parameter. It’s up to you to return an object that somehow uses `dispatch` to bind action creators in your own way. (Tip: you may use the [`bindActionCreators()`](https://redux.js.org/api-reference/bindactioncreators) helper from Redux.) + If a function is passed, it will be given `dispatch` as the first parameter. It’s up to you to return an object that somehow uses `dispatch` to bind action creators in your own way. (Tip: you may use the [`bindActionCreators()`](https://redux.js.org/api-reference/bindactioncreators) helper from Redux.) - If your `mapDispatchToProps` function is declared as taking two parameters, it will be called with `dispatch` as the first parameter and the props passed to the connected component as the second parameter, and will be re-invoked whenever the connected component receives new props. (The second parameter is normally referred to as `ownProps` by convention.) + If your `mapDispatchToProps` function is declared as taking two parameters, it will be called with `dispatch` as the first parameter and the props passed to the connected component as the second parameter, and will be re-invoked whenever the connected component receives new props. (The second parameter is normally referred to as `ownProps` by convention.) If you do not supply your own `mapDispatchToProps` function or object full of action creators, the default `mapDispatchToProps` implementation just injects `dispatch` into your component’s props. - > Note: in advanced scenarios where you need more control over the rendering performance, `mapDispatchToProps()` can also return a function. In this case, _that_ function will be used as `mapDispatchToProps()` for a particular component instance. This allows you to do per-instance memoization. You can refer to [#279](https://github.com/reduxjs/react-redux/pull/279) and the tests it adds for more details. Most apps never need this. + >Note: in advanced scenarios where you need more control over the rendering performance, `mapDispatchToProps()` can also return a function. In this case, *that* function will be used as `mapDispatchToProps()` for a particular component instance. This allows you to do per-instance memoization. You can refer to [#279](https://github.com/reduxjs/react-redux/pull/279) and the tests it adds for more details. Most apps never need this. -- [`mergeProps(stateProps, dispatchProps, ownProps): props`] \(_Function_): If specified, it is passed the result of `mapStateToProps()`, `mapDispatchToProps()`, and the parent `props`. The plain object you return from it will be passed as props to the wrapped component. You may specify this function to select a slice of the state based on props, or to bind action creators to a particular variable from props. If you omit it, `Object.assign({}, ownProps, stateProps, dispatchProps)` is used by default. +* [`mergeProps(stateProps, dispatchProps, ownProps): props`] \(*Function*): If specified, it is passed the result of `mapStateToProps()`, `mapDispatchToProps()`, and the parent `props`. The plain object you return from it will be passed as props to the wrapped component. You may specify this function to select a slice of the state based on props, or to bind action creators to a particular variable from props. If you omit it, `Object.assign({}, ownProps, stateProps, dispatchProps)` is used by default. -- [`options`] _(Object)_ If specified, further customizes the behavior of the connector. In addition to the options passable to `connectAdvanced()` (see those below), `connect()` accepts these additional options: - - [`pure`] _(Boolean)_: If true, `connect()` will avoid re-renders and calls to `mapStateToProps`, `mapDispatchToProps`, and `mergeProps` if the relevant state/props objects remain equal based on their respective equality checks. Assumes that the wrapped component is a “pure” component and does not rely on any input or state other than its props and the selected Redux store’s state. Default value: `true` - - [`areStatesEqual`] _(Function)_: When pure, compares incoming store state to its previous value. Default value: `strictEqual (===)` - - [`areOwnPropsEqual`] _(Function)_: When pure, compares incoming props to its previous value. Default value: `shallowEqual` - - [`areStatePropsEqual`] _(Function)_: When pure, compares the result of `mapStateToProps` to its previous value. Default value: `shallowEqual` - - [`areMergedPropsEqual`] _(Function)_: When pure, compares the result of `mergeProps` to its previous value. Default value: `shallowEqual` - - [`storeKey`] _(String)_: The key of the context from where to read the store. You probably only need this if you are in the inadvisable position of having multiple stores. Default value: `'store'` +* [`options`] *(Object)* If specified, further customizes the behavior of the connector. In addition to the options passable to `connectAdvanced()` (see those below), `connect()` accepts these additional options: + * [`pure`] *(Boolean)*: If true, `connect()` will avoid re-renders and calls to `mapStateToProps`, `mapDispatchToProps`, and `mergeProps` if the relevant state/props objects remain equal based on their respective equality checks. Assumes that the wrapped component is a “pure” component and does not rely on any input or state other than its props and the selected Redux store’s state. Default value: `true` + * [`areStatesEqual`] *(Function)*: When pure, compares incoming store state to its previous value. Default value: `strictEqual (===)` + * [`areOwnPropsEqual`] *(Function)*: When pure, compares incoming props to its previous value. Default value: `shallowEqual` + * [`areStatePropsEqual`] *(Function)*: When pure, compares the result of `mapStateToProps` to its previous value. Default value: `shallowEqual` + * [`areMergedPropsEqual`] *(Function)*: When pure, compares the result of `mergeProps` to its previous value. Default value: `shallowEqual` + * [`storeKey`] *(String)*: The key of the context from where to read the store. You probably only need this if you are in the inadvisable position of having multiple stores. Default value: `'store'` #### The arity of mapStateToProps and mapDispatchToProps determines whether they receive ownProps @@ -99,278 +99,238 @@ function mapStateToProps(state) { console.log(arguments[1]); // undefined } ``` - ```js const mapStateToProps = (state, ownProps = {}) => { console.log(state); // state console.log(ownProps); // undefined -}; +} ``` - Functions with no mandatory parameters or two parameters **will receive** `ownProps`. - ```js const mapStateToProps = (state, ownProps) => { console.log(state); // state console.log(ownProps); // ownProps -}; +} ``` - ```js function mapStateToProps() { console.log(arguments[0]); // state console.log(arguments[1]); // ownProps } ``` - ```js const mapStateToProps = (...args) => { console.log(args[0]); // state console.log(args[1]); // ownProps -}; +} ``` #### Optimizing connect when options.pure is true When `options.pure` is true, `connect` performs several equality checks that are used to avoid unnecessary calls to `mapStateToProps`, `mapDispatchToProps`, `mergeProps`, and ultimately to `render`. These include `areStatesEqual`, `areOwnPropsEqual`, `areStatePropsEqual`, and `areMergedPropsEqual`. While the defaults are probably appropriate 99% of the time, you may wish to override them with custom implementations for performance or other reasons. Here are several examples: -- You may wish to override `areStatesEqual` if your `mapStateToProps` function is computationally expensive and is also only concerned with a small slice of your state. For example: `areStatesEqual: (next, prev) => prev.entities.todos === next.entities.todos`; this would effectively ignore state changes for everything but that slice of state. +* You may wish to override `areStatesEqual` if your `mapStateToProps` function is computationally expensive and is also only concerned with a small slice of your state. For example: `areStatesEqual: (next, prev) => prev.entities.todos === next.entities.todos`; this would effectively ignore state changes for everything but that slice of state. -- You may wish to override `areStatesEqual` to always return false (`areStatesEqual: () => false`) if you have impure reducers that mutate your store state. (This would likely impact the other equality checks as well, depending on your `mapStateToProps` function.) +* You may wish to override `areStatesEqual` to always return false (`areStatesEqual: () => false`) if you have impure reducers that mutate your store state. (This would likely impact the other equality checks as well, depending on your `mapStateToProps` function.) -- You may wish to override `areOwnPropsEqual` as a way to whitelist incoming props. You'd also have to implement `mapStateToProps`, `mapDispatchToProps` and `mergeProps` to also whitelist props. (It may be simpler to achieve this other ways, for example by using [recompose's mapProps](https://github.com/acdlite/recompose/blob/master/docs/API.md#mapprops).) +* You may wish to override `areOwnPropsEqual` as a way to whitelist incoming props. You'd also have to implement `mapStateToProps`, `mapDispatchToProps` and `mergeProps` to also whitelist props. (It may be simpler to achieve this other ways, for example by using [recompose's mapProps](https://github.com/acdlite/recompose/blob/master/docs/API.md#mapprops).) -- You may wish to override `areStatePropsEqual` to use `strictEqual` if your `mapStateToProps` uses a memoized selector that will only return a new object if a relevant prop has changed. This would be a very slight performance improvement, since would avoid extra equality checks on individual props each time `mapStateToProps` is called. +* You may wish to override `areStatePropsEqual` to use `strictEqual` if your `mapStateToProps` uses a memoized selector that will only return a new object if a relevant prop has changed. This would be a very slight performance improvement, since would avoid extra equality checks on individual props each time `mapStateToProps` is called. -- You may wish to override `areMergedPropsEqual` to implement a `deepEqual` if your selectors produce complex props. ex: nested objects, new arrays, etc. (The deep equal check should be faster than just re-rendering.) +* You may wish to override `areMergedPropsEqual` to implement a `deepEqual` if your selectors produce complex props. ex: nested objects, new arrays, etc. (The deep equal check should be faster than just re-rendering.) ### Returns A higher-order React component class that passes state and action creators into your component derived from the supplied arguments. This is created by `connectAdvanced`, and details of this higher-order component are covered there. - #### Examples #### Inject just `dispatch` and don't listen to store ```js -export default connect()(TodoApp); +export default connect()(TodoApp) ``` -#### Inject all action creators (`addTodo`, `completeTodo`, ...) without subscribing to the store +#### Inject all action creators (`addTodo`, `completeTodo`, ...) without subscribing to the store ```js -import * as actionCreators from "./actionCreators"; +import * as actionCreators from './actionCreators' -export default connect( - null, - actionCreators -)(TodoApp); +export default connect(null, actionCreators)(TodoApp) ``` #### Inject `dispatch` and every field in the global state -> Don’t do this! It kills any performance optimizations because `TodoApp` will rerender after every state change. -> It’s better to have more granular `connect()` on several components in your view hierarchy that each only -> listen to a relevant slice of the state. +>Don’t do this! It kills any performance optimizations because `TodoApp` will rerender after every state change. +>It’s better to have more granular `connect()` on several components in your view hierarchy that each only +>listen to a relevant slice of the state. ```js -export default connect(state => state)(TodoApp); +export default connect(state => state)(TodoApp) ``` #### Inject `dispatch` and `todos` ```js function mapStateToProps(state) { - return { todos: state.todos }; + return { todos: state.todos } } -export default connect(mapStateToProps)(TodoApp); +export default connect(mapStateToProps)(TodoApp) ``` #### Inject `todos` and all action creators ```js -import * as actionCreators from "./actionCreators"; +import * as actionCreators from './actionCreators' function mapStateToProps(state) { - return { todos: state.todos }; + return { todos: state.todos } } -export default connect( - mapStateToProps, - actionCreators -)(TodoApp); +export default connect(mapStateToProps, actionCreators)(TodoApp) ``` #### Inject `todos` and all action creators (`addTodo`, `completeTodo`, ...) as `actions` ```js -import * as actionCreators from "./actionCreators"; -import { bindActionCreators } from "redux"; +import * as actionCreators from './actionCreators' +import { bindActionCreators } from 'redux' function mapStateToProps(state) { - return { todos: state.todos }; + return { todos: state.todos } } function mapDispatchToProps(dispatch) { - return { actions: bindActionCreators(actionCreators, dispatch) }; + return { actions: bindActionCreators(actionCreators, dispatch) } } -export default connect( - mapStateToProps, - mapDispatchToProps -)(TodoApp); +export default connect(mapStateToProps, mapDispatchToProps)(TodoApp) ``` -#### Inject `todos` and a specific action creator (`addTodo`) +#### Inject `todos` and a specific action creator (`addTodo`) ```js -import { addTodo } from "./actionCreators"; -import { bindActionCreators } from "redux"; +import { addTodo } from './actionCreators' +import { bindActionCreators } from 'redux' function mapStateToProps(state) { - return { todos: state.todos }; + return { todos: state.todos } } function mapDispatchToProps(dispatch) { - return bindActionCreators({ addTodo }, dispatch); + return bindActionCreators({ addTodo }, dispatch) } -export default connect( - mapStateToProps, - mapDispatchToProps -)(TodoApp); +export default connect(mapStateToProps, mapDispatchToProps)(TodoApp) ``` -#### Inject `todos` and specific action creators (`addTodo` and `deleteTodo`) with shorthand syntax +#### Inject `todos` and specific action creators (`addTodo` and `deleteTodo`) with shorthand syntax ```js -import { addTodo, deleteTodo } from "./actionCreators"; +import { addTodo, deleteTodo } from './actionCreators' function mapStateToProps(state) { - return { todos: state.todos }; + return { todos: state.todos } } const mapDispatchToProps = { addTodo, deleteTodo -}; +} -export default connect( - mapStateToProps, - mapDispatchToProps -)(TodoApp); +export default connect(mapStateToProps, mapDispatchToProps)(TodoApp) ``` #### Inject `todos`, todoActionCreators as `todoActions`, and counterActionCreators as `counterActions` ```js -import * as todoActionCreators from "./todoActionCreators"; -import * as counterActionCreators from "./counterActionCreators"; -import { bindActionCreators } from "redux"; +import * as todoActionCreators from './todoActionCreators' +import * as counterActionCreators from './counterActionCreators' +import { bindActionCreators } from 'redux' function mapStateToProps(state) { - return { todos: state.todos }; + return { todos: state.todos } } function mapDispatchToProps(dispatch) { return { todoActions: bindActionCreators(todoActionCreators, dispatch), counterActions: bindActionCreators(counterActionCreators, dispatch) - }; + } } -export default connect( - mapStateToProps, - mapDispatchToProps -)(TodoApp); +export default connect(mapStateToProps, mapDispatchToProps)(TodoApp) ``` #### Inject `todos`, and todoActionCreators and counterActionCreators together as `actions` ```js -import * as todoActionCreators from "./todoActionCreators"; -import * as counterActionCreators from "./counterActionCreators"; -import { bindActionCreators } from "redux"; +import * as todoActionCreators from './todoActionCreators' +import * as counterActionCreators from './counterActionCreators' +import { bindActionCreators } from 'redux' function mapStateToProps(state) { - return { todos: state.todos }; + return { todos: state.todos } } function mapDispatchToProps(dispatch) { return { - actions: bindActionCreators( - Object.assign({}, todoActionCreators, counterActionCreators), - dispatch - ) - }; + actions: bindActionCreators(Object.assign({}, todoActionCreators, counterActionCreators), dispatch) + } } -export default connect( - mapStateToProps, - mapDispatchToProps -)(TodoApp); +export default connect(mapStateToProps, mapDispatchToProps)(TodoApp) ``` #### Inject `todos`, and all todoActionCreators and counterActionCreators directly as props ```js -import * as todoActionCreators from "./todoActionCreators"; -import * as counterActionCreators from "./counterActionCreators"; -import { bindActionCreators } from "redux"; +import * as todoActionCreators from './todoActionCreators' +import * as counterActionCreators from './counterActionCreators' +import { bindActionCreators } from 'redux' function mapStateToProps(state) { - return { todos: state.todos }; + return { todos: state.todos } } function mapDispatchToProps(dispatch) { - return bindActionCreators( - Object.assign({}, todoActionCreators, counterActionCreators), - dispatch - ); + return bindActionCreators(Object.assign({}, todoActionCreators, counterActionCreators), dispatch) } -export default connect( - mapStateToProps, - mapDispatchToProps -)(TodoApp); +export default connect(mapStateToProps, mapDispatchToProps)(TodoApp) ``` #### Inject `todos` of a specific user depending on props ```js -import * as actionCreators from "./actionCreators"; +import * as actionCreators from './actionCreators' function mapStateToProps(state, ownProps) { - return { todos: state.todos[ownProps.userId] }; + return { todos: state.todos[ownProps.userId] } } -export default connect(mapStateToProps)(TodoApp); +export default connect(mapStateToProps)(TodoApp) ``` #### Inject `todos` of a specific user depending on props, and inject `props.userId` into the action ```js -import * as actionCreators from "./actionCreators"; +import * as actionCreators from './actionCreators' function mapStateToProps(state) { - return { todos: state.todos }; + return { todos: state.todos } } function mergeProps(stateProps, dispatchProps, ownProps) { return Object.assign({}, ownProps, { todos: stateProps.todos[ownProps.userId], - addTodo: text => dispatchProps.addTodo(ownProps.userId, text) - }); + addTodo: (text) => dispatchProps.addTodo(ownProps.userId, text) + }) } -export default connect( - mapStateToProps, - actionCreators, - mergeProps -)(TodoApp); +export default connect(mapStateToProps, actionCreators, mergeProps)(TodoApp) ``` #### Factory functions @@ -410,32 +370,32 @@ export default connect(mapStateToPropsFactory, mapDispatchToPropsFactory)(TodoAp ## connectAdvanced ```js -connectAdvanced(selectorFactory, [connectOptions]); +connectAdvanced(selectorFactory, [connectOptions]) ``` Connects a React component to a Redux store. It is the base for `connect()` but is less opinionated about how to combine `state`, `props`, and `dispatch` into your final props. It makes no assumptions about defaults or memoization of results, leaving those responsibilities to the caller. -It does not modify the component class passed to it; instead, it _returns_ a new, connected component class for you to use. +It does not modify the component class passed to it; instead, it *returns* a new, connected component class for you to use. ### Arguments -- `selectorFactory(dispatch, factoryOptions): selector(state, ownProps): props` \(_Function_): Initializes a selector function (during each instance's constructor). That selector function is called any time the connector component needs to compute new props, as a result of a store state change or receiving new props. The result of `selector` is expected to be a plain object, which is passed as the props to the wrapped component. If a consecutive call to `selector` returns the same object (`===`) as its previous call, the component will not be re-rendered. It's the responsibility of `selector` to return that previous object when appropriate. +* `selectorFactory(dispatch, factoryOptions): selector(state, ownProps): props` \(*Function*): Initializes a selector function (during each instance's constructor). That selector function is called any time the connector component needs to compute new props, as a result of a store state change or receiving new props. The result of `selector` is expected to be a plain object, which is passed as the props to the wrapped component. If a consecutive call to `selector` returns the same object (`===`) as its previous call, the component will not be re-rendered. It's the responsibility of `selector` to return that previous object when appropriate. -- [`connectOptions`] _(Object)_ If specified, further customizes the behavior of the connector. +* [`connectOptions`] *(Object)* If specified, further customizes the behavior of the connector. - - [`getDisplayName`] _(Function)_: computes the connector component's displayName property relative to that of the wrapped component. Usually overridden by wrapper functions. Default value: `name => 'ConnectAdvanced('+name+')'` + * [`getDisplayName`] *(Function)*: computes the connector component's displayName property relative to that of the wrapped component. Usually overridden by wrapper functions. Default value: `name => 'ConnectAdvanced('+name+')'` - - [`methodName`] _(String)_: shown in error messages. Usually overridden by wrapper functions. Default value: `'connectAdvanced'` + * [`methodName`] *(String)*: shown in error messages. Usually overridden by wrapper functions. Default value: `'connectAdvanced'` - - [`renderCountProp`] _(String)_: if defined, a property named this value will be added to the props passed to the wrapped component. Its value will be the number of times the component has been rendered, which can be useful for tracking down unnecessary re-renders. Default value: `undefined` + * [`renderCountProp`] *(String)*: if defined, a property named this value will be added to the props passed to the wrapped component. Its value will be the number of times the component has been rendered, which can be useful for tracking down unnecessary re-renders. Default value: `undefined` - - [`shouldHandleStateChanges`] _(Boolean)_: controls whether the connector component subscribes to redux store state changes. If set to false, it will only re-render when parent component re-renders. Default value: `true` + * [`shouldHandleStateChanges`] *(Boolean)*: controls whether the connector component subscribes to redux store state changes. If set to false, it will only re-render when parent component re-renders. Default value: `true` - - [`storeKey`] _(String)_: the key of props/context to get the store. You probably only need this if you are in the inadvisable position of having multiple stores. Default value: `'store'` + * [`storeKey`] *(String)*: the key of props/context to get the store. You probably only need this if you are in the inadvisable position of having multiple stores. Default value: `'store'` - - [`withRef`] _(Boolean)_: If true, stores a ref to the wrapped component instance and makes it available via `getWrappedInstance()` method. Default value: `false` + * [`withRef`] *(Boolean)*: If true, stores a ref to the wrapped component instance and makes it available via `getWrappedInstance()` method. Default value: `false` - - Additionally, any extra options passed via `connectOptions` will be passed through to your `selectorFactory` in the `factoryOptions` argument. + * Additionally, any extra options passed via `connectOptions` will be passed through to your `selectorFactory` in the `factoryOptions` argument. @@ -445,7 +405,7 @@ A higher-order React component class that builds props from the store state and #### Static Properties -- `WrappedComponent` _(Component)_: The original component class passed to `connectAdvanced(...)(Component)`. +* `WrappedComponent` *(Component)*: The original component class passed to `connectAdvanced(...)(Component)`. #### Static Methods @@ -459,58 +419,56 @@ Returns the wrapped component instance. Only available if you pass `{ withRef: t ### Remarks -- Since `connectAdvanced` returns a higher-order component, it needs to be invoked two times. The first time with its arguments as described above, and a second time, with the component: `connectAdvanced(selectorFactory)(MyComponent)`. +* Since `connectAdvanced` returns a higher-order component, it needs to be invoked two times. The first time with its arguments as described above, and a second time, with the component: `connectAdvanced(selectorFactory)(MyComponent)`. -- `connectAdvanced` does not modify the passed React component. It returns a new, connected component, that you should use instead. +* `connectAdvanced` does not modify the passed React component. It returns a new, connected component, that you should use instead. - #### Examples #### Inject `todos` of a specific user depending on props, and inject `props.userId` into the action ```js -import * as actionCreators from "./actionCreators"; -import { bindActionCreators } from "redux"; +import * as actionCreators from './actionCreators' +import { bindActionCreators } from 'redux' function selectorFactory(dispatch) { - let ownProps = {}; - let result = {}; - const actions = bindActionCreators(actionCreators, dispatch); - const addTodo = text => actions.addTodo(ownProps.userId, text); + let ownProps = {} + let result = {} + const actions = bindActionCreators(actionCreators, dispatch) + const addTodo = (text) => actions.addTodo(ownProps.userId, text) return (nextState, nextOwnProps) => { - const todos = nextState.todos[nextOwnProps.userId]; - const nextResult = { ...nextOwnProps, todos, addTodo }; - ownProps = nextOwnProps; - if (!shallowEqual(result, nextResult)) result = nextResult; - return result; - }; + const todos = nextState.todos[nextOwnProps.userId] + const nextResult = { ...nextOwnProps, todos, addTodo } + ownProps = nextOwnProps + if (!shallowEqual(result, nextResult)) result = nextResult + return result + } } -export default connectAdvanced(selectorFactory)(TodoApp); +export default connectAdvanced(selectorFactory)(TodoApp) ``` ## createProvider ```js -createProvider([storeKey]); +createProvider([storeKey]) ``` Creates a new `` which will set the Redux Store on the passed key of the context. You probably only need this if you are in the inadvisable position of having multiple stores. You will also need to pass the same `storeKey` to the `options` argument of [`connect`](#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options) ### Arguments -- [`storeKey`](*String*): The key of the context on which to set the store. Default value: `'store'` +* [`storeKey`] (*String*): The key of the context on which to set the store. Default value: `'store'` ### Examples - Before creating multiple stores, please go through this FAQ: [Can or should I create multiple stores?](http://redux.js.org/docs/faq/StoreSetup.html#can-or-should-i-create-multiple-stores-can-i-import-my-store-directly-and-use-it-in-components-myself) ```js -import { connect, createProvider } from "react-redux"; +import {connect, createProvider} from 'react-redux' -const STORE_KEY = "componentStore"; +const STORE_KEY = 'componentStore' -export const Provider = createProvider(STORE_KEY); +export const Provider = createProvider(STORE_KEY) function connectExtended( mapStateToProps, @@ -518,16 +476,15 @@ function connectExtended( mergeProps, options = {} ) { - options.storeKey = STORE_KEY; + options.storeKey = STORE_KEY return connect( mapStateToProps, mapDispatchToProps, mergeProps, options - ); + ) } -export { connectExtended as connect }; +export {connectExtended as connect} ``` - Now you can import the above `Provider` and `connect` and use them. diff --git a/docs/using-react-redux/connect-extracting-data-with-mapStateToProps.md b/docs/using-react-redux/connect-extracting-data-with-mapStateToProps.md index 3aee5d05a..402ecfa4b 100644 --- a/docs/using-react-redux/connect-extracting-data-with-mapStateToProps.md +++ b/docs/using-react-redux/connect-extracting-data-with-mapStateToProps.md @@ -6,12 +6,12 @@ sidebar_label: Connect: Extracting Data with mapStateToProps --- # 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: @@ -22,7 +22,7 @@ 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 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`. +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. @@ -30,24 +30,24 @@ This function should be passed as the first argument to `connect`, and will be w #### `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 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. +The `mapStateToProps` function should always be written with at least `state` passed in. ```js -// TodoList.js +// 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`. +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`. ```js // Todo.js @@ -59,18 +59,18 @@ function mapStateToProps(state, ownProps) { // component receives additionally: return { todo, visibilityFilter }; -} +}; // Later, in your application, a parent component renders: -; + // 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. +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: +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 @@ -80,53 +80,64 @@ For example: ```js function mapStateToProps(state) { return { - a: 42, - todos: state.todos, - filter: state.visibilityFilter - }; + 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](https://github.com/reduxjs/react-redux/pull/279) and the tests it adds. Most apps never need this. + +> 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](https://github.com/reduxjs/react-redux/pull/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. +`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.) +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. +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. -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`. +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. +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 | +| `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 `mapState` 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. +React-Redux does shallow comparisons to see if the `mapState` 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: @@ -135,11 +146,13 @@ Many common operations result in new object or array references being created: - 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, `mapState` will still return the same result values as before, and `connect` can skip re-rendering. +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, `mapState` 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. +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: @@ -147,6 +160,7 @@ There are a few ways to approach this: - 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](https://twitter.com/leeb/status/746733697093668864?lang=en): @@ -155,13 +169,18 @@ Immutable.js author Lee Byron on Twitter [explicitly advises avoiding `toJS` whe 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 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 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 @@ -169,26 +188,27 @@ With just `(state)`, the function runs whenever the root store state object is d 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`**. + +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`: ```js function mapStateToProps(state) { - console.log(state); // state + console.log(state); // state console.log(arguments[1]); // undefined } const mapStateToProps = (state, ownProps = {}) => { - console.log(state); // state + 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: ```js function mapStateToProps(state, ownProps) { - console.log(state); // state + console.log(state); // state console.log(ownProps); // ownProps } From f24ca6dff9bf6ac59c5b95ab70ad9a6791d08d49 Mon Sep 17 00:00:00 2001 From: Wei Gao Date: Wed, 10 Oct 2018 11:15:20 +0800 Subject: [PATCH 4/4] Add `array.slice` to new references list --- .../connect-extracting-data-with-mapStateToProps.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/using-react-redux/connect-extracting-data-with-mapStateToProps.md b/docs/using-react-redux/connect-extracting-data-with-mapStateToProps.md index 402ecfa4b..f1ca2557e 100644 --- a/docs/using-react-redux/connect-extracting-data-with-mapStateToProps.md +++ b/docs/using-react-redux/connect-extracting-data-with-mapStateToProps.md @@ -143,6 +143,7 @@ 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 }`