From 7e2c35d23dd8b84c1725a59718a119beaa9a3f7c Mon Sep 17 00:00:00 2001 From: Wei Gao Date: Thu, 20 Dec 2018 15:52:07 +0800 Subject: [PATCH 01/14] Add `connect()` doc --- docs/api/connect.md | 609 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 609 insertions(+) create mode 100644 docs/api/connect.md diff --git a/docs/api/connect.md b/docs/api/connect.md new file mode 100644 index 000000000..dd2a0c414 --- /dev/null +++ b/docs/api/connect.md @@ -0,0 +1,609 @@ +--- +id: connect +title: Connect +sidebar_label: connect() +hide_title: true +--- + +# `connect()` + +## Overview + +`connect()` Connects a React component to a Redux store. + +It provides its connected component with part of the data it needs from the store, and the functions it can use to dispatch actions to the store. + +It does not modify the component class passed to it; instead, it returns a new, connected component class that wraps the component you passed in. + +```JavaScript +function connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?) +``` + +The `mapStateToProps` and `mapDispatchToProps` deals with your Redux store’s `state` and `dispatch`, respectively. `state` and `dispatch` will be supplied to your `mapStateToProps` or `mapDispatchToProps` functions as the first argument. + +The returns of `mapStateToProps` and `mapDispatchToProps` are regarded as `stateProps` and `dispatchProps`, respectively. They will be supplied to `mergeProps`, if defined, as the first and the second argument, where the third argument will be `ownProps`. The combined result, commonly referred to as `mergedProps`, will then be supplied to your connected component. + +## `connect()` Parameters + +`connect` accepts four different parameters, all optional. Conventionally, they are called: + +1. `mapStateToProps?: Function` +2. `mapDispatchToProps?: Function | Object` +3. `mergeProps?: Function` +4. `options?: Object` + + +### `mapStateToProps?: (state, ownProps?) => Object` + +The component will only subscribe to the store and receive updates if you supply a `mapStateToProps` function. i.e., the following component will *not* subscribe to the store: + +```JavaScript +import { connect } from 'react-redux'; + +const MyComponent = (props) => (
A component not subscribed to store
); +// but it will receive `dispatch` + +export default connect()(MyComponent); +``` + +If a `mapStateToProps` function 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`. + +#### Parameters + +1. `state: Object` +2. `ownProps?: Object` + +A `mapStateToProps` function takes a maximum of two parameters. The number of declared function parameters (a.k.a. arity) affects when it will be called. This also determines whether the function will receive ownProps. See notes [here](#the-arity-of-maptoprops-functions). + +##### `state` + +If your `mapStateToProps` function is declared as taking one parameter, it will be called with the store state as the parameter. And will be called whenever the store changes. + +```JavaScript +const mapStateToProps = (state) => ({ todos: state.todos }); +```` + +##### `ownProps` + +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. + +```JavaScript +const mapStateToProps = (state, ownProps) => ({ todo: state.todos[ownProps.id] }); +``` + +#### Returns + +Your `mapStateToProps` functions are expected to return an object. This object, normally referred to as `stateProps`, will be merged as props to your connected component. If you define `mergeProps`, it will be supplied as the first parameter to `mergeProps`. + +The return of the `mapStateToProps` determine whether the connected component will re-render (details [here](../using-react-redux/connect-mapstate#return-values-determine-if-your-component-re-renders)). + +For more details on recommended usage of `mapStateToProps`, please refer to [our guide on using `mapStateToProps`](../using-react-redux/connect-mapstate). + +> You may define `mapStateToProps` and `mapDispatchToProps` as a factory function, i.e., you return a function instead of an object. In this case your returned function will be treated as the real `mapStateToProps` or `mapDispatchToProps`, and be called in subsequent calls. You may see notes on [Factory Functions](#factory-functions) or our guide on performance optimizations. + + +### `mapDispatchToProps?: Object | (dispatch, ownProps?) => Object` + +Conventionally called `mapDispatchToProps`, this second parameter to `connect()` may either be an object, a function, or not supplied. + +Your component will receive `dispatch` by default, i.e., when you do not supply a second parameter to `connect()`: + +```JavaScript +// do not pass `mapDispatchToProps` +connect()(MyComponent); +connect(mapDispatch)(MyComponent); +connect(mapDispatch, null, mergeProps, options)(MyComponent); +``` + +If you define a `mapDispatchToProps` as a function, it will be called with maximum of two parameters. + +#### Parameters + + +1. `dispatch: Function` +2. `ownProps?: Object` + +##### `dispatch` + +If your `mapDispatchToProps` is declared as taking one parameter, it will be given the `dispatch` of your `store`. + +```JavaScript +const mapDispatchToProps = dispatch => { + return { + // dispatching plain actions + increment: () => dispatch({ type: "INCREMENT" }), + decrement: () => dispatch({ type: "DECREMENT" }), + reset: () => dispatch({ type: "RESET" }) + }; +}; +``` + +##### `ownProps` + +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. + +```JavaScript +// binds on component re-rendering + - ); + ) } } export default connect( null, { addTodo } -)(AddTodo); +)(AddTodo) ``` Now our `` is connected to the store. When we add a todo it would dispatch an action to change the store. We are not seeing it in the app because the other components are not connected yet. If you have the Redux DevTools Extension hooked up, you should see the action being dispatched: @@ -253,16 +253,16 @@ Luckily we have a selector that does exactly this. We may simply import the sele ```js // redux/selectors.js -export const getTodosState = store => store.todos; +export const getTodosState = store => store.todos export const getTodoList = store => - getTodosState(store) ? getTodosState(store).allIds : []; + getTodosState(store) ? getTodosState(store).allIds : [] export const getTodoById = (store, id) => - getTodosState(store) ? { ...getTodosState(store).byIds[id], id } : {}; + getTodosState(store) ? { ...getTodosState(store).byIds[id], id } : {} export const getTodos = store => - getTodoList(store).map(id => getTodoById(store, id)); + getTodoList(store).map(id => getTodoById(store, id)) ``` ```js @@ -303,7 +303,7 @@ If you call `connect` without providing any arguments, your component will: ```js // ... Component -export default connect()(Component); // Component will receive `dispatch` (just like our !) +export default connect()(Component) // Component will receive `dispatch` (just like our !) ``` #### Subscribe to the store and do not inject action creators @@ -315,8 +315,8 @@ If you call `connect` with only `mapStateToProps`, your component will: ```js // ... Component -const mapStateToProps = state => state.partOfState; -export default connect(mapStateToProps)(Component); +const mapStateToProps = state => state.partOfState +export default connect(mapStateToProps)(Component) ``` #### Do not subscribe to the store and inject action creators @@ -327,12 +327,12 @@ If you call `connect` with only `mapDispatchToProps`, your component will: - receive each of the action creators you inject with `mapDispatchToProps` as props and automatically dispatch the actions upon being called ```js -import { addTodo } from "./actionCreators"; +import { addTodo } from './actionCreators' // ... Component export default connect( null, { addTodo } -)(Component); +)(Component) ``` #### Subscribe to the store and inject action creators @@ -343,13 +343,13 @@ If you call `connect` with both `mapStateToProps` and `mapDispatchToProps`, your - receive all of the action creators you inject with `mapDispatchToProps` as props and automatically dispatch the actions upon being called. ```js -import * as actionCreators from "./actionCreators"; +import * as actionCreators from './actionCreators' // ... Component -const mapStateToProps = state => state.partOfState; +const mapStateToProps = state => state.partOfState export default connect( mapStateToProps, actionCreators -)(Component); +)(Component) ``` These four cases cover the most basic usages of `connect`. To read more about `connect`, continue reading our [API section](../api/connect.md) that explains it in more detail. @@ -410,17 +410,17 @@ Meanwhile, we also need to update our `` component to filter todos a // ... other selectors export const getTodosByVisibilityFilter = (store, visibilityFilter) => { - const allTodos = getTodos(store); + const allTodos = getTodos(store) switch (visibilityFilter) { case VISIBILITY_FILTERS.COMPLETED: - return allTodos.filter(todo => todo.completed); + return allTodos.filter(todo => todo.completed) case VISIBILITY_FILTERS.INCOMPLETE: - return allTodos.filter(todo => !todo.completed); + return allTodos.filter(todo => !todo.completed) case VISIBILITY_FILTERS.ALL: default: - return allTodos; + return allTodos } -}; +} ``` And connecting to the store with the help of the selector: @@ -431,12 +431,12 @@ And connecting to the store with the help of the selector: // ... const mapStateToProps = state => { - const { visibilityFilter } = state; - const todos = getTodosByVisibilityFilter(state, visibilityFilter); - return { todos }; -}; + const { visibilityFilter } = state + const todos = getTodosByVisibilityFilter(state, visibilityFilter) + return { todos } +} -export default connect(mapStateToProps)(TodoList); +export default connect(mapStateToProps)(TodoList) ``` Now we've finished a very simple example of a todo app with React Redux. All our components are connected! Isn't that nice? 🎉🎊 diff --git a/docs/introduction/quick-start.md b/docs/introduction/quick-start.md index 611b45f91..1f6edb8d1 100644 --- a/docs/introduction/quick-start.md +++ b/docs/introduction/quick-start.md @@ -32,21 +32,21 @@ You'll also need to [install Redux](https://redux-docs.netlify.com/introduction/ React Redux provides ``, which makes the Redux store available to the rest of your app: ```js -import React from "react"; -import ReactDOM from "react-dom"; +import React from 'react' +import ReactDOM from 'react-dom' -import { Provider } from "react-redux"; -import store from "./store"; +import { Provider } from 'react-redux' +import store from './store' -import App from "./App"; +import App from './App' -const rootElement = document.getElementById("root"); +const rootElement = document.getElementById('root') ReactDOM.render( , rootElement -); +) ``` React Redux provides a `connect` function for you to connect your component to the store. @@ -54,23 +54,23 @@ React Redux provides a `connect` function for you to connect your component to t Normally, you’ll call `connect` in this way: ```js -import { connect } from "react-redux"; -import { increment, decrement, reset } from "./actionCreators"; +import { connect } from 'react-redux' +import { increment, decrement, reset } from './actionCreators' // const Counter = ... const mapStateToProps = (state /*, ownProps*/) => { return { counter: state.counter - }; -}; + } +} -const mapDispatchToProps = { increment, decrement, reset }; +const mapDispatchToProps = { increment, decrement, reset } export default connect( mapStateToProps, mapDispatchToProps -)(Counter); +)(Counter) ``` ## Help and Discussion diff --git a/docs/introduction/why-use-react-redux.md b/docs/introduction/why-use-react-redux.md index a46a129be..f18fc7195 100644 --- a/docs/introduction/why-use-react-redux.md +++ b/docs/introduction/why-use-react-redux.md @@ -7,11 +7,11 @@ sidebar_label: Why Use React Redux? # Why Use React Redux? -Redux itself is a standalone library that can be used with any UI layer or framework, including React, Angular, Vue, Ember, and vanilla JS. Although Redux and React are commonly used together, they are independent of each other. +Redux itself is a standalone library that can be used with any UI layer or framework, including React, Angular, Vue, Ember, and vanilla JS. Although Redux and React are commonly used together, they are independent of each other. If you are using Redux with any kind of UI framework, you will normally use a "UI binding" library to tie Redux together with your UI framework, rather than directly interacting with the store from your UI code. -**React Redux is the official Redux UI binding library for React**. If you are using Redux and React together, you should also use React Redux to bind these two libraries. +**React Redux is the official Redux UI binding library for React**. If you are using Redux and React together, you should also use React Redux to bind these two libraries. To understand why you should use React Redux, it may help to understand what a "UI binding library" does. @@ -22,7 +22,6 @@ To understand why you should use React Redux, it may help to understand what a " > - [You Might Not Need Redux](https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367) > - [Idiomatic Redux: The Tao of Redux, Part 1 - Implementation and Intent](https://blog.isquaredsoftware.com/2017/05/idiomatic-redux-tao-of-redux-part-1/) - ## Integrating Redux with a UI Using Redux with _any_ UI layer requires [the same consistent set of steps](https://blog.isquaredsoftware.com/presentations/workshops/redux-fundamentals/ui-layer.html#/4): @@ -30,69 +29,61 @@ Using Redux with _any_ UI layer requires [the same consistent set of steps](http 1. Create a Redux store 2. Subscribe to updates 3. Inside the subscription callback: - 1. Get the current store state - 2. Extract the data needed by this piece of UI - 3. Update the UI with the data + 1. Get the current store state + 2. Extract the data needed by this piece of UI + 3. Update the UI with the data 4. If necessary, render the UI with initial state 5. Respond to UI inputs by dispatching Redux actions -While it is possible to write this logic by hand, doing so would become very repetitive. In addition, optimizing UI performance would require complicated logic. +While it is possible to write this logic by hand, doing so would become very repetitive. In addition, optimizing UI performance would require complicated logic. -The process of subscribing to the store, checking for updated data, and triggering a re-render can be made more generic and reusable. **A UI binding library like React Redux handles the store interaction logic, so you don't have to write that code yourself.** +The process of subscribing to the store, checking for updated data, and triggering a re-render can be made more generic and reusable. **A UI binding library like React Redux handles the store interaction logic, so you don't have to write that code yourself.** > **Note**: For a deeper look at how React Redux works internally and how it handles the store interaction for you, see **[Idiomatic Redux: The History and Implementation of React Redux](https://blog.isquaredsoftware.com/2018/11/react-redux-history-implementation/)**. - ## Reasons to Use React Redux ### It is the Official Redux UI Bindings for React -While Redux can be used with any UI layer, it was originally designed and intended for use with React. There are [UI binding layers for many other frameworks](https://redux.js.org/introduction/ecosystem#library-integration-and-bindings), but React Redux is maintained directly by the Redux team. - -As the offical Redux binding for React, React Redux is kept up-to-date with any API changes from either library, to ensure that your React components behave as expected. Its intended usage adopts the design principles of React - writing declarative components. +While Redux can be used with any UI layer, it was originally designed and intended for use with React. There are [UI binding layers for many other frameworks](https://redux.js.org/introduction/ecosystem#library-integration-and-bindings), but React Redux is maintained directly by the Redux team. +As the offical Redux binding for React, React Redux is kept up-to-date with any API changes from either library, to ensure that your React components behave as expected. Its intended usage adopts the design principles of React - writing declarative components. ### It Encourages Good React Architecture -React components are a lot like functions. While it's possible to write all your code in a single function, it's usually better to split that logic into smaller functions that each handle a specific task, making them easier to understand. +React components are a lot like functions. While it's possible to write all your code in a single function, it's usually better to split that logic into smaller functions that each handle a specific task, making them easier to understand. -Similarly, while you can write large React components that handle many different tasks, it's usually better to split up components based on responsibilities. In particular, it is common to have "container" components that are responsible for collecting and managing some kind of data, and "presentational" components that simply display UI based on whatever data they've received as props. +Similarly, while you can write large React components that handle many different tasks, it's usually better to split up components based on responsibilities. In particular, it is common to have "container" components that are responsible for collecting and managing some kind of data, and "presentational" components that simply display UI based on whatever data they've received as props. -**The React Redux `connect` function generates "container" wrapper components that handle the process of interacting with the store for you**. That way, your own components can focus on other tasks, whether it be collecting other data, or just displaying a piece of the UI. In addition, **`connect` abstracts away the question of _which_ store is being used, making your own components more reusable**. - -As a general architectural principle, **we want to keep our own components "unaware" of Redux**. They should simply receive data and functions as props, just like any other React component. This ultimately makes it easier to test and reuse your own components. +**The React Redux `connect` function generates "container" wrapper components that handle the process of interacting with the store for you**. That way, your own components can focus on other tasks, whether it be collecting other data, or just displaying a piece of the UI. In addition, **`connect` abstracts away the question of _which_ store is being used, making your own components more reusable**. +As a general architectural principle, **we want to keep our own components "unaware" of Redux**. They should simply receive data and functions as props, just like any other React component. This ultimately makes it easier to test and reuse your own components. ### It Implements Performance Optimizations For You -React is generally fast, but by default any updates to a component will cause React to re-render all of the components inside that part of the component tree. This does require work, and if the data for a given component hasn't changed, then re-rendering is likely some wasted effort because the requested UI output would be the same. - -If performance is a concern, the best way to improve performance is to skip unnecessary re-renders, so that components only re-render when their data has actually changed. **React Redux implements many performance optimizations internally, so that your own component only re-renders when it actually needs to.** +React is generally fast, but by default any updates to a component will cause React to re-render all of the components inside that part of the component tree. This does require work, and if the data for a given component hasn't changed, then re-rendering is likely some wasted effort because the requested UI output would be the same. -In addition, by connecting multiple components in your React component tree, you can ensure that each connected component only extracts the specific pieces of data from the store state that are needed by that component. This means that your own component will need to re-render less often, because most of the time those specific pieces of data haven't changed. +If performance is a concern, the best way to improve performance is to skip unnecessary re-renders, so that components only re-render when their data has actually changed. **React Redux implements many performance optimizations internally, so that your own component only re-renders when it actually needs to.** +In addition, by connecting multiple components in your React component tree, you can ensure that each connected component only extracts the specific pieces of data from the store state that are needed by that component. This means that your own component will need to re-render less often, because most of the time those specific pieces of data haven't changed. ### Community Support -As the official binding library for React and Redux, React Redux has a large community of users. This makes it easier to ask for help, learn about best practices, use libraries that build on top of React Redux, and reuse your knowledge across different applications. - - +As the official binding library for React and Redux, React Redux has a large community of users. This makes it easier to ask for help, learn about best practices, use libraries that build on top of React Redux, and reuse your knowledge across different applications. ## Links and References - ### Understanding React Redux - [Idiomatic Redux: The History and Implementation of React Redux](https://blog.isquaredsoftware.com/2018/11/react-redux-history-implementation/) - [`connect.js` Explained](https://gist.github.com/gaearon/1d19088790e70ac32ea636c025ba424e) - [Redux Fundamentals workshop slides](https://blog.isquaredsoftware.com/2018/06/redux-fundamentals-workshop-slides/) - - [UI Layer Integration](https://blog.isquaredsoftware.com/presentations/workshops/redux-fundamentals/ui-layer.html) - - [Using React Redux](https://blog.isquaredsoftware.com/presentations/workshops/redux-fundamentals/react-redux.html) - + - [UI Layer Integration](https://blog.isquaredsoftware.com/presentations/workshops/redux-fundamentals/ui-layer.html) + - [Using React Redux](https://blog.isquaredsoftware.com/presentations/workshops/redux-fundamentals/react-redux.html) ### Community Resources -- Discord channel: [#redux on Reactiflux](https://gist.github.com/gaearon/1d19088790e70ac32ea636c025ba424e) ([Reactiflux invite link](https://reactiflux.com)) +- Discord channel: [#redux on Reactiflux](https://gist.github.com/gaearon/1d19088790e70ac32ea636c025ba424e) ([Reactiflux invite link](https://reactiflux.com)) - Stack Overflow topics: [Redux](https://stackoverflow.com/questions/tagged/redux), [React Redux](https://stackoverflow.com/questions/tagged/redux) - Reddit: [/r/reactjs](https://www.reddit.com/r/reactjs/), [/r/reduxjs](https://www.reddit.com/r/reduxjs/) - Github issues (bug reports and feature requests): https://github.com/reduxjs/react-redux/issues diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 83270fa47..d52a29924 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -10,7 +10,8 @@ hide_title: true Make sure to check out [Troubleshooting Redux](http://redux.js.org/docs/Troubleshooting.html) first. ### I'm getting the following alert: Accessing PropTypes via the main React package is deprecated. Use the prop-types package from npm instead. -This warning is shown when using react 15.5.*. Basically, now it's just a warning, but in react16 the application might break. the PropTypes should now be imported from 'prop-types' package, and not from the react package. + +This warning is shown when using react 15.5.\*. Basically, now it's just a warning, but in react16 the application might break. the PropTypes should now be imported from 'prop-types' package, and not from the react package. Update to the latest version of react-redux. @@ -19,8 +20,8 @@ Update to the latest version of react-redux. See the link above. In short, -* Reducers should never mutate state, they must return new objects, or React Redux won’t see the updates. -* Make sure you either bind action creators with the `mapDispatchToProps` argument to `connect()` or with the `bindActionCreators()` method, or that you manually call `dispatch()`. Just calling your `MyActionCreators.addTodo()` function won’t work because it just *returns* an action, but does not *dispatch* it. +- Reducers should never mutate state, they must return new objects, or React Redux won’t see the updates. +- Make sure you either bind action creators with the `mapDispatchToProps` argument to `connect()` or with the `bindActionCreators()` method, or that you manually call `dispatch()`. Just calling your `MyActionCreators.addTodo()` function won’t work because it just _returns_ an action, but does not _dispatch_ it. ### My views aren’t updating on route change with React Router 0.13 @@ -29,7 +30,8 @@ If you’re using React Router 0.13, you might [bump into this problem](https:// Root view: ```jsx -Router.run(routes, Router.HistoryLocation, (Handler, routerState) => { // note "routerState" here +Router.run(routes, Router.HistoryLocation, (Handler, routerState) => { + // note "routerState" here ReactDOM.render( {/* note "routerState" here */} @@ -56,7 +58,7 @@ You can also upgrade to React Router 1.0 which shouldn’t have this problem. (L If your views depend on global state or [React “context”](http://facebook.github.io/react/docs/context.html), you might find that views decorated with `connect()` will fail to update. ->This is because `connect()` implements [shouldComponentUpdate](https://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate) by default, assuming that your component will produce the same results given the same props and state. This is a similar concept to React’s [PureRenderMixin](https://facebook.github.io/react/docs/pure-render-mixin.html). +> This is because `connect()` implements [shouldComponentUpdate](https://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate) by default, assuming that your component will produce the same results given the same props and state. This is a similar concept to React’s [PureRenderMixin](https://facebook.github.io/react/docs/pure-render-mixin.html). The _best_ solution to this is to make sure that your components are pure and pass any external state to them via props. This will ensure that your views do not re-render unless they actually need to re-render and will greatly speed up your application. @@ -67,9 +69,14 @@ function mapStateToProps(state) { return { todos: state.todos } } -export default connect(mapStateToProps, null, null, { - pure: false -})(TodoApp) +export default connect( + mapStateToProps, + null, + null, + { + pure: false + } +)(TodoApp) ``` This will remove the assumption that `TodoApp` is pure and cause it to update whenever its parent component renders. Note that this will make your application less performant, so only do this if you have no other option. diff --git a/docs/using-react-redux/connect-dispatching-actions-with-mapDispatchToProps.md b/docs/using-react-redux/connect-dispatching-actions-with-mapDispatchToProps.md index b56b4af93..9734f4620 100644 --- a/docs/using-react-redux/connect-dispatching-actions-with-mapDispatchToProps.md +++ b/docs/using-react-redux/connect-dispatching-actions-with-mapDispatchToProps.md @@ -27,15 +27,15 @@ The `mapDispatchToProps` functions are normally referred to as `mapDispatch` for If you don't specify the second argument to `connect()`, your component will receive `dispatch` by default. For example: ```js -connect()(MyComponent); +connect()(MyComponent) // which is equivalent with connect( null, null -)(MyComponent); +)(MyComponent) // or -connect(mapStateToProps /** no second argument */)(MyComponent); +connect(mapStateToProps /** no second argument */)(MyComponent) ``` Once you have connected your component in this way, your component receives `props.dispatch`. You may use it to dispatch actions to the store. @@ -44,12 +44,12 @@ Once you have connected your component in this way, your component receives `pro function Counter({ count, dispatch }) { return (
- + {count} - - + +
- ); + ) } ``` @@ -89,7 +89,7 @@ const TodoList = ({ todos, toggleTodo }) => ( ))} -); +) ``` This is what React Redux’s `connect` does — it encapsulates the logic of talking to the Redux store and lets you not worry about it. And this is what you should totally make full use of in your implementation. @@ -123,11 +123,11 @@ You will normally make use of this by returning new functions that call `dispatc const mapDispatchToProps = dispatch => { return { // dispatching plain actions - increment: () => dispatch({ type: "INCREMENT" }), - decrement: () => dispatch({ type: "DECREMENT" }), - reset: () => dispatch({ type: "RESET" }) - }; -}; + increment: () => dispatch({ type: 'INCREMENT' }), + decrement: () => dispatch({ type: 'DECREMENT' }), + reset: () => dispatch({ type: 'RESET' }) + } +} ``` You will also likely want to forward arguments to your action creators: @@ -141,8 +141,8 @@ const mapDispatchToProps = dispatch => { // implicitly forwarding arguments onReceiveImpressions: (...impressions) => dispatch(trackImpressions(impressions)) - }; -}; + } +} ``` **`ownProps` ( optional )** @@ -153,12 +153,12 @@ This means, instead of re-binding new `props` to action dispatchers upon compone ```js // binds on component re-rendering - - ); + ) } ``` @@ -214,18 +214,21 @@ Wrapping these functions by hand is tedious, so Redux provides a function to sim The wrapper functions generated by `bindActionCreators` will automatically forward all of their arguments, so you don't need to do that by hand. ```js -import { bindActionCreators } from "redux"; +import { bindActionCreators } from 'redux' -const increment = () => ({ type: "INCREMENT" }); -const decrement = () => ({ type: "DECREMENT" }); -const reset = () => ({ type: "RESET" }); +const increment = () => ({ type: 'INCREMENT' }) +const decrement = () => ({ type: 'DECREMENT' }) +const reset = () => ({ type: 'RESET' }) // binding an action creator // returns (...args) => dispatch(increment(...args)) -const boundIncrement = bindActionCreators(increment, dispatch); +const boundIncrement = bindActionCreators(increment, dispatch) // binding an object full of action creators -const boundActionCreators = bindActionCreators({ increment, decrement, reset }, dispatch); +const boundActionCreators = bindActionCreators( + { increment, decrement, reset }, + dispatch +) // returns // { // increment: (...args) => dispatch(increment(...args)), @@ -237,18 +240,18 @@ const boundActionCreators = bindActionCreators({ increment, decrement, reset }, To use `bindActionCreators` in our `mapDispatchToProps` function: ```js -import { bindActionCreators } from "redux"; +import { bindActionCreators } from 'redux' // ... function mapDispatchToProps(dispatch) { - return bindActionCreators({ increment, decrement, reset }, dispatch); + return bindActionCreators({ increment, decrement, reset }, dispatch) } // component receives props.increment, props.decrement, props.reset connect( null, mapDispatchToProps -)(Counter); +)(Counter) ``` ### Manually Injecting `dispatch` @@ -256,14 +259,14 @@ connect( If the `mapDispatchToProps` argument is supplied, the component will no longer receive the default `dispatch`. You may bring it back by adding it manually to the return of your `mapDispatchToProps`, although most of the time you shouldn’t need to do this: ```js -import { bindActionCreators } from "redux"; +import { bindActionCreators } from 'redux' // ... function mapDispatchToProps(dispatch) { return { dispatch, ...bindActionCreators({ increment, decrement, reset }, dispatch) - }; + } } ``` @@ -282,7 +285,7 @@ Note that: ```js // React Redux does this for you automatically: -dispatch => bindActionCreators(mapDispatchToProps, dispatch); +dispatch => bindActionCreators(mapDispatchToProps, dispatch) ``` Therefore, our `mapDispatchToProps` can simply be: @@ -292,7 +295,7 @@ const mapDispatchToProps = { increment, decrement, reset -}; +} ``` Since the actual name of the variable is up to you, you might want to give it a name like `actionCreators`, or even define the object inline in the call to `connect`: @@ -337,7 +340,7 @@ In another words, if you do: ```js // component receives `dispatch` -connect(mapStateToProps /** no second argument*/)(Component); +connect(mapStateToProps /** no second argument*/)(Component) ``` **2. Your customized `mapDispatchToProps` function return specifically contains `dispatch`** @@ -351,20 +354,20 @@ const mapDispatchToProps = dispatch => { decrement: () => dispatch(decrement()), reset: () => dispatch(reset()), dispatch - }; -}; + } +} ``` Or alternatively, with `bindActionCreators`: ```js -import { bindActionCreators } from "redux"; +import { bindActionCreators } from 'redux' function mapDispatchToProps(dispatch) { return { dispatch, ...bindActionCreators({ increment, decrement, reset }, dispatch) - }; + } } ``` @@ -380,7 +383,7 @@ Yes. You can skip the first parameter by passing `undefined` or `null`. Your com connect( null, mapDispatchToProps -)(MyComponent); +)(MyComponent) ``` ### Can I call `store.dispatch`? 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 e6bdf0255..d304d96b2 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: @@ -33,47 +33,47 @@ This function should be passed as the first argument to `connect`, and will be c #### `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); + 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 function mapStateToProps(state, ownProps) { - const { visibilityFilter } = state; - const { id } = ownProps; - const todo = getTodoById(state, id); + const { visibilityFilter } = state + const { id } = ownProps + const todo = getTodoById(state, id) // component receives additionally: - return { todo, visibilityFilter }; -}; + 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 @@ -83,52 +83,42 @@ 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. - -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. +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`. - - +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: @@ -137,10 +127,9 @@ To summarize the behavior of the component wrapped by `connect` with `mapStateTo | `mapStateToProps` runs when: | store `state` changes | store `state` changes
or
any field of `ownProps` is different | | component re-renders when: | any field of `stateProps` is different | any field of `stateProps` is different
or
any field of `ownProps` is different | - ### Only Return New Object References If Needed -React Redux does shallow comparisons to see if the `mapStateToProps` results have changed. It’s easy to accidentally return new object or array references every time, which would cause your component to re-render even if the data is actually the same. +React Redux does shallow comparisons to see if the `mapStateToProps` results have changed. It’s easy to accidentally return new object or array references every time, which would cause your component to re-render even if the data is actually the same. Many common operations result in new object or array references being created: @@ -150,13 +139,11 @@ 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, `mapStateToProps` 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, `mapStateToProps` will still return the same result values as before, and `connect` can skip re-rendering. ### Only Perform Expensive Operations When Data Changes -Transforming data can often be expensive (_and_ usually results in new object references being created). In order for your `mapStateToProps` function to be as fast as possible, you should only re-run these complex transformations when the relevant data has changed. +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: @@ -164,7 +151,6 @@ 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): @@ -173,18 +159,13 @@ 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 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 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 @@ -192,19 +173,18 @@ 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(arguments[1]); // undefined + console.log(state) // state + console.log(arguments[1]) // undefined } const mapStateToProps = (state, ownProps = {}) => { - console.log(state); // state - console.log(ownProps); // undefined + console.log(state) // state + console.log(ownProps) // undefined } ``` @@ -212,18 +192,18 @@ It _will_ receive `ownProps` when the formal definition of the function contains ```js function mapStateToProps(state, ownProps) { - console.log(state); // state - console.log(ownProps); // ownProps + console.log(state) // state + console.log(ownProps) // ownProps } function mapStateToProps() { - console.log(arguments[0]); // state - console.log(arguments[1]); // ownProps + console.log(arguments[0]) // state + console.log(arguments[1]) // ownProps } function mapStateToProps(...args) { - console.log(args[0]); // state - console.log(args[1]); // ownProps + console.log(args[0]) // state + console.log(args[1]) // ownProps } ``` From 6ca59ce2045c9992d12ccdb2ef57467826243031 Mon Sep 17 00:00:00 2001 From: Mark Erikson Date: Sun, 23 Dec 2018 15:45:52 -0500 Subject: [PATCH 14/14] Remove stray semi --- docs/api/connect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/connect.md b/docs/api/connect.md index bcccef298..d901081c4 100644 --- a/docs/api/connect.md +++ b/docs/api/connect.md @@ -121,7 +121,7 @@ The second parameter is normally referred to as `ownProps` by convention. ```js // binds on component re-rendering -;