File: README.md
React Redux
Official React bindings for Redux.
Performant and flexible.
Installation
Create a React Redux App
The recommended way to start new apps with React and Redux is by using our official Redux+TS template for Vite, or by creating a new Next.js project using Next's with-redux template.
Both of these already have Redux Toolkit and React-Redux configured appropriately for that build tool, and come with a small example app that demonstrates how to use several of Redux Toolkit's features.
# Vite with our Redux+TS template
# (using the `degit` tool to clone and extract the template)
npx degit reduxjs/redux-templates/packages/vite-template-redux my-app
# Next.js using the `with-redux` template
npx create-next-app --example with-redux my-appAn Existing React App
React Redux 9.0 requires React 18 or later
To use React Redux with your React app, install it as a dependency:
# If you use npm:
npm install react-redux
# Or if you use Yarn:
yarn add react-reduxYou'll also need to install Redux and set up a Redux store in your app.
This assumes that youβre using npm package manager
with a module bundler like Webpack or
Browserify to consume CommonJS
modules.
If you donβt yet use npm or a modern module bundler, and would rather prefer a single-file UMD build that makes ReactRedux available as a global object, you can grab a pre-built version from cdnjs. We donβt recommend this approach for any serious application, as most of the libraries complementary to Redux are only available on npm.
Documentation
The React Redux docs are published at https://react-redux.js.org .
How Does It Work?
The post The History and Implementation of React-Redux
explains what it does, how it works, and how the API and implementation have evolved over time.
There's also a Deep Dive into React-Redux talk that covers some of the same material at a higher level.
License
File: docs/introduction/getting-started.md
id: getting-started title: Getting Started with React Redux hide_title: true sidebar_label: Getting Started
import LiteYouTubeEmbed from 'react-lite-youtube-embed';
import 'react-lite-youtube-embed/dist/LiteYouTubeEmbed.css'
Getting Started with React Redux
React Redux is the official React UI bindings layer for Redux. It lets your React components read data from a Redux store, and dispatch actions to the store to update state.
Installation
React Redux 8.x requires React 16.8.3 or later / React Native 0.59 or later, in order to make use of React Hooks.
Create a React Redux App
The recommended way to start new apps with React and Redux is by using our official Redux+TS template for Vite, or by creating a new Next.js project using Next's with-redux template.
Both of these already have Redux Toolkit and React-Redux configured appropriately for that build tool, and come with a small example app that demonstrates how to use several of Redux Toolkit's features.
# Vite with our Redux+TS template
# (using the `degit` tool to clone and extract the template)
npx degit reduxjs/redux-templates/packages/vite-template-redux my-app
# Next.js using the `with-redux` template
npx create-next-app --example with-redux my-appWe do not currently have official React Native templates, but recommend these templates for standard React Native and for Expo:
- https://github.com/rahsheen/react-native-template-redux-typescript
- https://github.com/rahsheen/expo-template-redux-typescript
An Existing React App
To use React Redux with your React app, install it as a dependency:
# If you use npm:
npm install react-redux
# Or if you use Yarn:
yarn add react-reduxYou'll also need to install Redux and set up a Redux store in your app.
React-Redux v8 is written in TypeScript, so all types are automatically included.
API Overview
`Provider`
React Redux includes a `` component, which makes the Redux store available to the rest of your app:
import React from 'react'
import ReactDOM from 'react-dom/client'
import { Provider } from 'react-redux'
import store from './store'
import App from './App'
// As of React 18
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<App />
,
)Hooks
React Redux provides a pair of custom React hooks that allow your React components to interact with the Redux store.
useSelector reads a value from the store state and subscribes to updates, while useDispatch returns the store's dispatch method to let you dispatch actions.
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import {
decrement,
increment,
incrementByAmount,
incrementAsync,
selectCount,
} from './counterSlice'
import styles from './Counter.module.css'
export function Counter() {
const count = useSelector(selectCount)
const dispatch = useDispatch()
return (
dispatch(increment())}
>
+
{count}
dispatch(decrement())}
>
-
{/* omit additional rendering output here */}
)
}Learning React Redux
Learn Modern Redux Livestream
Redux maintainer Mark Erikson appeared on the "Learn with Jason" show to explain how we recommend using Redux today. The show includes a live-coded example app that shows how to use Redux Toolkit and React-Redux hooks with TypeScript, as well as the new RTK Query data fetching APIs.
See the "Learn Modern Redux" show notes page for a transcript and links to the example app source.
Help and Discussion
The #redux channel of the Reactiflux Discord community is our official resource for all questions related to learning and using Redux. Reactiflux is a great place to hang out, ask questions, and learn - come join us!
You can also ask questions on Stack Overflow using the #redux tag.
Docs Translations
File: docs/introduction/why-use-react-redux.md
id: why-use-react-redux title: Why Use React Redux? hide_title: true 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.
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.
To understand why you should use React Redux, it may help to understand what a "UI binding library" does.
:::info
If you have questions about whether you should use Redux in general, please see these articles for discussion of when and why you might want to use Redux, and how it's intended to be used:
- Redux docs: Motivation
- Redux docs: FAQ - When should I use Redux?
- You Might Not Need Redux
- Idiomatic Redux: The Tao of Redux, Part 1 - Implementation and Intent
:::
Integrating Redux with a UI
Using Redux with any UI layer requires the same consistent set of steps:
- Create a Redux store
- Subscribe to updates
- Inside the subscription callback:
- Get the current store state
- Extract the data needed by this piece of UI
- Update the UI with the data
- If necessary, render the UI with initial state
- 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.
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.
:::info
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.
:::
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, but React Redux is maintained directly by the Redux team.
As the official 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 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.
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.
Links and References
Understanding React Redux
- Idiomatic Redux: The History and Implementation of React Redux
connect.jsExplained- Redux Fundamentals workshop slides
Community Resources
- Discord channel: #redux on Reactiflux (Reactiflux invite link)
- Stack Overflow topics: Redux, React Redux
- Reddit: /r/reactjs, /r/reduxjs
- GitHub issues (bug reports and feature requests): https://github.com/reduxjs/react-redux/issues
- Tutorials, articles, and further resources: React/Redux Links
File: docs/api/batch.md
id: batch title: batch sidebar_label: batch() hide_title: true
`batch()`
batch((fn: () => void))added in v7.0.0
:::info
If you're using React 18, you do not need to use the batch API. React 18 automatically batches all state updates, no matter where they're queued.
:::
React's unstable_batchedUpdates() API allows any React updates in an event loop tick to be batched together into a single render pass. React already uses this internally for its own event handler callbacks. This API is actually part of the renderer packages like ReactDOM and React Native, not the React core itself.
Since React-Redux needs to work in both ReactDOM and React Native environments, we've taken care of importing this API from the correct renderer at build time for our own use. We also now re-export this function publicly ourselves, renamed to batch(). You can use it to ensure that multiple actions dispatched outside of React only result in a single render update, like this:
import { batch } from 'react-redux'
function myThunk() {
return (dispatch, getState) => {
// should only result in one combined re-render, not two
batch(() => {
dispatch(increment())
dispatch(increment())
})
}
}References
unstable_batchedUpdates()API from React- React 18 Working Group: Automatic Batching for Fewer Renders in React 18
File: docs/api/connect.md
id: connect title: Connect sidebar_label: connect() hide_title: true
`connect()`
:::tip
connect still works and is supported in React-Redux 8.x. However, we recommend using the hooks API as the default.
:::
Overview
The connect() function connects a React component to a Redux store.
It provides its connected component with the pieces 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.
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 referred to internally 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. By convention, they are called:
mapStateToProps?: FunctionmapDispatchToProps?: Function | ObjectmergeProps?: Functionoptions?: Object
`mapStateToProps?: (state, ownProps?) => Object`
If a mapStateToProps function is specified, the new wrapper 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 wrapped componentβs props. If you don't want to subscribe to store updates, pass null or undefined in place of mapStateToProps.
Parameters
state: ObjectownProps?: 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.
`state`
If your mapStateToProps function is declared as taking one parameter, it will be called whenever the store state changes, and given the store state as the only parameter.
const mapStateToProps = (state) => ({ todos: state.todos })`ownProps`
If your mapStateToProps function is declared as taking two parameters, it will be called whenever the store state changes or when the wrapper component receives new props (based on shallow equality comparisons). It will be given the store state as the first parameter, and the wrapper component's props as the second parameter.
The second parameter is normally referred to as ownProps by convention.
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).
For more details on recommended usage of mapStateToProps, please refer to our guide on using mapStateToProps.
You may define
mapStateToPropsandmapDispatchToPropsas a factory function, i.e., you return a function instead of an object. In this case your returned function will be treated as the realmapStateToPropsormapDispatchToProps, and be called in subsequent calls. You may see notes on 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():
// do not pass `mapDispatchToProps`
connect()(MyComponent)
connect(mapState)(MyComponent)
connect(mapState, null, mergeProps, options)(MyComponent)If you define a mapDispatchToProps as a function, it will be called with a maximum of two parameters.
Parameters
dispatch: FunctionownProps?: Object
`dispatch`
If your mapDispatchToProps is declared as a function taking one parameter, it will be given the dispatch of your store.
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 wrapper 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.
// binds on component re-rendering
this.props.toggleTodo(this.props.todoId)} />
// binds on `props` change
const mapDispatchToProps = (dispatch, ownProps) => ({
toggleTodo: () => dispatch(toggleTodo(ownProps.todoId)),
})The number of declared function parameters of mapDispatchToProps determines whether they receive ownProps. See notes here.
Returns
Your mapDispatchToProps functions are expected to return an object. Each fields of the object should be a function, calling which is expected to dispatch an action to the store.
The return of your mapDispatchToProps functions are regarded as dispatchProps. It will be merged as props to your connected component. If you define mergeProps, it will be supplied as the second parameter to mergeProps.
const createMyAction = () => ({ type: 'MY_ACTION' })
const mapDispatchToProps = (dispatch, ownProps) => {
const boundActions = bindActionCreators({ createMyAction }, dispatch)
return {
dispatchPlainObject: () => dispatch({ type: 'MY_ACTION' }),
dispatchActionCreatedByActionCreator: () => dispatch(createMyAction()),
...boundActions,
// you may return dispatch here
dispatch,
}
}For more details on recommended usage, please refer to our guide on using mapDispatchToProps.
You may define
mapStateToPropsandmapDispatchToPropsas a factory function, i.e., you return a function instead of an object. In this case your returned function will be treated as the realmapStateToPropsormapDispatchToProps, and be called in subsequent calls. You may see notes on Factory Functions or our guide on performance optimizations.
Object Shorthand Form
mapDispatchToProps may be an object where each field is an action creator.
import { addTodo, deleteTodo, toggleTodo } from './actionCreators'
const mapDispatchToProps = {
addTodo,
deleteTodo,
toggleTodo,
}
export default connect(null, mapDispatchToProps)(TodoApp)In this case, React-Redux binds the dispatch of your store to each of the action creators using bindActionCreators. The result will be regarded as dispatchProps, which will be either directly merged to your connected components, or supplied to mergeProps as the second argument.
// internally, React-Redux calls bindActionCreators
// to bind the action creators to the dispatch of your store
bindActionCreators(mapDispatchToProps, dispatch)We also have a section in our mapDispatchToProps guide on the usage of object shorthand form here.
`mergeProps?: (stateProps, dispatchProps, ownProps) => Object`
If specified, defines how the final props for your own wrapped component are determined. If you do not provide mergeProps, your wrapped component receives { ...ownProps, ...stateProps, ...dispatchProps } by default.
Parameters
mergeProps should be specified with maximum of three parameters. They are the result of mapStateToProps(), mapDispatchToProps(), and the wrapper component's props, respectively:
statePropsdispatchPropsownProps
The fields in the plain object you return from it will be used as the props for 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.
Returns
The return value of mergeProps is referred to as mergedProps and the fields will be used as the props for the wrapped component.
Note: Creating new values in mergeProps will cause re-renders. It is recommended that you memoize fields in order to avoid unnecessary re-renders.
`options?: Object`
{
context?: Object,
areStatesEqual?: Function,
areOwnPropsEqual?: Function,
areStatePropsEqual?: Function,
areMergedPropsEqual?: Function,
forwardRef?: boolean,
}`context: Object`
Note: This parameter is supported in >= v6.0 only
React-Redux v6 allows you to supply a custom context instance to be used by React-Redux.
You need to pass the instance of your context to both `` and your connected component.
You may pass the context to your connected component either by passing it here as a field of option, or as a prop to your connected component in rendering.
// const MyContext = React.createContext();
connect(mapStateToProps, mapDispatchToProps, null, { context: MyContext })(
MyComponent,
)`areStatesEqual: (next: Object, prev: Object, nextOwnProps: Object, prevOwnProps: Object) => boolean`
- default value:
strictEqual: (next, prev) => prev === next
Compares incoming store state to its previous value.
const areStatesEqual = (next, prev) =>
prev.entities.todos === next.entities.todosYou may wish to override areStatesEqual if your mapStateToProps function is computationally expensive and is also only concerned with a small slice of your state. The example above will effectively ignore state changes for everything but that slice of state. Additionally, areStatesEqual provides nextOwnProps and prevOwnProps to allow for more effective scoping of your state which your connected component is interested in, if needed.
This would likely impact the other equality checks as well, depending on your mapStateToProps function.
`areOwnPropsEqual: (next: Object, prev: Object) => boolean`
- default value:
shallowEqual: (objA, objB) => boolean
( returnstruewhen each field of the objects is equal )
Compares incoming props to its previous value.
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.)
`areStatePropsEqual: (next: Object, prev: Object) => boolean`
- type:
function - default value:
shallowEqual
Compares the result of mapStateToProps to its previous value.
`areMergedPropsEqual: (next: Object, prev: Object) => boolean`
- default value:
shallowEqual
Compares the result of mergeProps to its previous value.
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 may be faster than just re-rendering.)
`forwardRef: boolean`
Note: This parameter is supported in >= v6.0 only
If {forwardRef : true} has been passed to connect, adding a ref to the connected wrapper component will actually return the instance of the wrapped component.
`connect()` Returns
The return of connect() is a wrapper function that takes your component and returns a wrapper component with the additional props it injects.
import { login, logout } from './actionCreators'
const mapState = (state) => state.user
const mapDispatch = { login, logout }
// first call: returns a hoc that you can use to wrap any component
const connectUser = connect(mapState, mapDispatch)
// second call: returns the wrapper component with mergedProps
// you may use the hoc to enable different components to get the same behavior
const ConnectedUserLogin = connectUser(Login)
const ConnectedUserProfile = connectUser(Profile)In most cases, the wrapper function will be called right away, without being saved in a temporary variable:
import { login, logout } from './actionCreators'
const mapState = (state) => state.user
const mapDispatch = { login, logout }
// call connect to generate the wrapper function, and immediately call
// the wrapper function to generate the final wrapper component.
export default connect(mapState, mapDispatch)(Login)Example Usage
Because connect is so flexible, it may help to see some additional examples of how it can be called:
- Inject just
dispatchand don't listen to store
export default connect()(TodoApp)- Inject all action creators (
addTodo,completeTodo, ...) without subscribing to the store
import * as actionCreators from './actionCreators'
export default connect(null, actionCreators)(TodoApp)- Inject
dispatchand every field in the global state
Donβt do this! It kills any performance optimizations because
TodoAppwill rerender after every state change.
Itβs better to have more granularconnect()on several components in your view hierarchy that each only listen to a relevant slice of the state.
// don't do this!
export default connect((state) => state)(TodoApp)- Inject
dispatchandtodos
function mapStateToProps(state) {
return { todos: state.todos }
}
export default connect(mapStateToProps)(TodoApp)- Inject
todosand all action creators
import * as actionCreators from './actionCreators'
function mapStateToProps(state) {
return { todos: state.todos }
}
export default connect(mapStateToProps, actionCreators)(TodoApp)- Inject
todosand all action creators (addTodo,completeTodo, ...) asactions
import * as actionCreators from './actionCreators'
import { bindActionCreators } from 'redux'
function mapStateToProps(state) {
return { todos: state.todos }
}
function mapDispatchToProps(dispatch) {
return { actions: bindActionCreators(actionCreators, dispatch) }
}
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)- Inject
todosand a specific action creator (addTodo)
import { addTodo } from './actionCreators'
import { bindActionCreators } from 'redux'
function mapStateToProps(state) {
return { todos: state.todos }
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ addTodo }, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)- Inject
todosand specific action creators (addTodoanddeleteTodo) with shorthand syntax
import { addTodo, deleteTodo } from './actionCreators'
function mapStateToProps(state) {
return { todos: state.todos }
}
const mapDispatchToProps = {
addTodo,
deleteTodo,
}
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)- Inject
todos,todoActionCreatorsastodoActions, andcounterActionCreatorsascounterActions
import * as todoActionCreators from './todoActionCreators'
import * as counterActionCreators from './counterActionCreators'
import { bindActionCreators } from 'redux'
function mapStateToProps(state) {
return { todos: state.todos }
}
function mapDispatchToProps(dispatch) {
return {
todoActions: bindActionCreators(todoActionCreators, dispatch),
counterActions: bindActionCreators(counterActionCreators, dispatch),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)- Inject
todos, and todoActionCreators and counterActionCreators together asactions
import * as todoActionCreators from './todoActionCreators'
import * as counterActionCreators from './counterActionCreators'
import { bindActionCreators } from 'redux'
function mapStateToProps(state) {
return { todos: state.todos }
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(
{ ...todoActionCreators, ...counterActionCreators },
dispatch,
),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)- Inject
todos, and alltodoActionCreatorsandcounterActionCreatorsdirectly as props
import * as todoActionCreators from './todoActionCreators'
import * as counterActionCreators from './counterActionCreators'
import { bindActionCreators } from 'redux'
function mapStateToProps(state) {
return { todos: state.todos }
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(
{ ...todoActionCreators, ...counterActionCreators },
dispatch,
)
}
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)- Inject
todosof a specific user depending on props
import * as actionCreators from './actionCreators'
function mapStateToProps(state, ownProps) {
return { todos: state.todos[ownProps.userId] }
}
export default connect(mapStateToProps)(TodoApp)- Inject
todosof a specific user depending on props, and injectprops.userIdinto the action
import * as actionCreators from './actionCreators'
function mapStateToProps(state) {
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),
})
}
export default connect(mapStateToProps, actionCreators, mergeProps)(TodoApp)Notes
The Arity of `mapToProps` Functions
The number of declared function parameters of mapStateToProps and mapDispatchToProps determines whether they receive ownProps
Note:
ownPropsis not passed tomapStateToPropsandmapDispatchToPropsif the formal definition of the function contains one mandatory parameter (function has length 1). For example, functions defined like below won't receiveownPropsas the second argument. If the incoming value ofownPropsisundefined, the default argument value will be used.
function mapStateToProps(state) {
console.log(state) // state
console.log(arguments[1]) // undefined
}
const mapStateToProps = (state, ownProps = {}) => {
console.log(state) // state
console.log(ownProps) // {}
}Functions with no mandatory parameters or two parameters*will receive ownProps.
const mapStateToProps = (state, ownProps) => {
console.log(state) // state
console.log(ownProps) // ownProps
}
function mapStateToProps() {
console.log(arguments[0]) // state
console.log(arguments[1]) // ownProps
}
const mapStateToProps = (...args) => {
console.log(args[0]) // state
console.log(args[1]) // ownProps
}Factory Functions
If your mapStateToProps or mapDispatchToProps functions return a function, they will be called once when the component instantiates, and their returns will be used as the actual mapStateToProps, mapDispatchToProps, functions respectively, in their subsequent calls.
The factory functions are commonly used with memoized selectors. This gives you the ability to create component-instance-specific selectors inside the closure:
const makeUniqueSelectorInstance = () =>
createSelector([selectItems, selectItemId], (items, itemId) => items[itemId])
const makeMapState = (state) => {
const selectItemForThisComponent = makeUniqueSelectorInstance()
return function realMapState(state, ownProps) {
const item = selectItemForThisComponent(state, ownProps.itemId)
return { item }
}
}
export default connect(makeMapState)(SomeComponent)Legacy Version Docs
While the connect API has stayed almost entirely API-compatible between all of our major versions, there have been some small changes in options and behavior from version to version.
For details on the legacy 5.x and 6.x versions, please see these archived files in the React Redux repo:
File: docs/api/hooks.md
id: hooks title: Hooks sidebar_label: Hooks hide_title: true
Hooks
React's "hooks" APIs give function components the ability to use local component state, execute side effects, and more. React also lets us write custom hooks, which let us extract reusable hooks to add our own behavior on top of React's built-in hooks.
React Redux includes its own custom hook APIs, which allow your React components to subscribe to the Redux store and dispatch actions.
:::tip
We recommend using the React-Redux hooks API as the default approach in your React components.
The existing connect API still works and will continue to be supported, but the hooks API is simpler and works better with TypeScript.
:::
These hooks were first added in v7.1.0.
Using Hooks in a React Redux App
As with connect(), you should start by wrapping your entire application in a `` component to make the store available throughout the component tree:
const store = createStore(rootReducer)
// As of React 18
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<App />
,
)From there, you may import any of the listed React Redux hooks APIs and use them within your function components.
`useSelector()`
type RootState = ReturnType<typeof store.getState>
type SelectorFn = <Selected>(state: RootState) => Selected
type EqualityFn = (a: any, b: any) => boolean
export type DevModeCheckFrequency = 'never' | 'once' | 'always'
interface UseSelectorOptions {
equalityFn?: EqualityFn
devModeChecks?: {
stabilityCheck?: DevModeCheckFrequency
identityFunctionCheck?: DevModeCheckFrequency
}
}
const result: Selected = useSelector(
selector: SelectorFn,
options?: EqualityFn | UseSelectorOptions
)Allows you to extract data from the Redux store state for use in this component, using a selector function.
:::info
The selector function should be pure since it is potentially executed multiple times and at arbitrary points in time.
See Using Redux: Deriving Data with Selectors in the Redux docs for more details on writing and using selector functions.
:::
The selector will be called with the entire Redux store state as its only argument. The selector may return any value as a result, including directly returning a value that was nested inside state, or deriving new values. The return value of the selector will be used as the return value of the useSelector() hook.
The selector will be run whenever the function component renders (unless its reference hasn't changed since a previous render of the component so that a cached result can be returned by the hook without re-running the selector). useSelector() will also subscribe to the Redux store, and run your selector whenever an action is dispatched.
When an action is dispatched, useSelector() will do a reference comparison of the previous selector result value and the current result value. If they are different, the component will be forced to re-render. If they are the same, the component will not re-render. useSelector() uses strict === reference equality checks by default, not shallow equality (see the following section for more details).
The selector is approximately equivalent to the mapStateToProps argument to connect conceptually.
You may call useSelector() multiple times within a single function component. Each call to useSelector() creates an individual subscription to the Redux store. Because of the React update batching behavior used in React Redux v7, a dispatched action that causes multiple useSelector()s in the same component to return new values should only result in a single re-render.
:::info
There are potential edge cases with using props in selectors that may cause issues. See the Usage Warnings section of this page for further details.
:::
Equality Comparisons and Updates
When the function component renders, the provided selector function will be called and its result will be returned
from the useSelector() hook. (A cached result may be returned by the hook without re-running the selector if it's the same function reference as on a previous render of the component.)
However, when an action is dispatched to the Redux store, useSelector() only forces a re-render if the selector result
appears to be different than the last result. The default comparison is a strict === reference
comparison. This is different than connect(), which uses shallow equality checks on the results of mapState calls
to determine if re-rendering is needed. This has several implications on how you should use useSelector().
With mapState, all individual fields were returned in a combined object. It didn't matter if the return object was
a new reference or not - connect() just compared the individual fields. With useSelector(), returning a new object
every time will always force a re-render by default. If you want to retrieve multiple values from the store, you can:
- Call
useSelector()multiple times, with each call returning a single field value - Use Reselect or a similar library to create a memoized selector that returns multiple values in one object, but
only returns a new object when one of the values has changed. - Use the
shallowEqualfunction from React-Redux as theequalityFnargument touseSelector(), like:
import { shallowEqual, useSelector } from 'react-redux'
// Pass it as the second argument directly
const selectedData = useSelector(selectorReturningObject, shallowEqual)
// or pass it as the `equalityFn` field in the options argument
const selectedData = useSelector(selectorReturningObject, {
equalityFn: shallowEqual,
})- Use a custom equality function as the
equalityFnargument touseSelector(), like:
import { useSelector } from 'react-redux'
// equality function
const customEqual = (oldValue, newValue) => oldValue === newValue
// later
const selectedData = useSelector(selectorReturningObject, customEqual)The optional comparison function also enables using something like Lodash's _.isEqual() or Immutable.js's comparison capabilities.
`useSelector` Examples
Basic usage:
import React from 'react'
import { useSelector } from 'react-redux'
export const CounterComponent = () => {
const counter = useSelector((state) => state.counter)
return {counter}
}Using props via closure to determine what to extract:
import React from 'react'
import { useSelector } from 'react-redux'
export const TodoListItem = (props) => {
const todo = useSelector((state) => state.todos[props.id])
return {todo.text}
}Using memoizing selectors
When using useSelector with an inline selector as shown above, a new instance of the selector is created whenever the component is rendered. This works as long as the selector does not maintain any state. However, memoizing selectors (e.g. created via createSelector from reselect) do have internal state, and therefore care must be taken when using them. Below you can find typical usage scenarios for memoizing selectors.
When the selector does only depend on the state, simply ensure that it is declared outside of the component so that the same selector instance is used for each render:
import React from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect'
const selectNumCompletedTodos = createSelector(
(state) => state.todos,
(todos) => todos.filter((todo) => todo.completed).length,
)
export const CompletedTodosCounter = () => {
const numCompletedTodos = useSelector(selectNumCompletedTodos)
return {numCompletedTodos}
}
export const App = () => {
return (
<>
Number of completed todos:
<CompletedTodosCounter />
</>
)
}The same is true if the selector depends on the component's props, but will only ever be used in a single instance of a single component:
import React from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect'
const selectCompletedTodosCount = createSelector(
(state) => state.todos,
(_, completed) => completed,
(todos, completed) =>
todos.filter((todo) => todo.completed === completed).length,
)
export const CompletedTodosCount = ({ completed }) => {
const matchingCount = useSelector((state) =>
selectCompletedTodosCount(state, completed),
)
return {matchingCount}
}
export const App = () => {
return (
<>
Number of done todos:
<CompletedTodosCount completed={true} />
</>
)
}However, when the selector is used in multiple component instances and depends on the component's props, you need to ensure that selector's memoization behavior is properly configured (see here for details).
Development mode checks
useSelector runs some extra checks in development mode to watch for unexpected behavior. These checks do not run in production builds.
:::info
These checks were first added in v8.1.0
:::
Selector result stability
In development, the provided selector function is run an extra time with the same parameter during the first call to useSelector, and warns in the console if the selector returns a different result (based on the equalityFn provided).
This is important, as a selector that returns a different result reference when called again with the same inputs will cause unnecessary rerenders.
// this selector will return a new object reference whenever called,
// which causes the component to rerender after *every* action is dispatched
const { count, user } = useSelector((state) => ({
count: state.count,
user: state.user,
}))If a selector result is suitably stable (or the selector is memoized), it will not return a different result and no warning will be logged.
By default, this will only happen when the selector is first called. You can configure the check in the Provider or at each useSelector call.
{children}function Component() {
const count = useSelector(selectCount, {
devModeChecks: { stabilityCheck: 'never' },
})
// run once (default)
const user = useSelector(selectUser, {
devModeChecks: { stabilityCheck: 'once' },
})
// ...
}Identity Function (`state => state`) Check
:::danger Breaking Change!
This was previously referred to as noopCheck.
:::
In development, a check is conducted on the result returned by the selector. It warns in the console if the result is the same as the parameter passed in, i.e. the root state.
A useSelector call returning the entire root state is almost always a mistake, as it means the component will rerender whenever anything in state changes. Selectors should be as granular as possible, like state => state.some.nested.field.
// BAD: this selector returns the entire state, meaning that the component will rerender unnecessarily
const { count, user } = useSelector((state) => state)
// GOOD: instead, select only the state you need, calling useSelector as many times as needed
const count = useSelector((state) => state.count.value)
const user = useSelector((state) => state.auth.currentUser)By default, this will only happen when the selector is first called. You can configure the check in the Provider or at each useSelector call.
{children}function Component() {
const count = useSelector(selectCount, {
devModeChecks: { identityFunctionCheck: 'never' },
})
// run once (default)
const user = useSelector(selectUser, {
devModeChecks: { identityFunctionCheck: 'once' },
})
// ...
}Comparisons with `connect`
There are some differences between the selectors passed to useSelector() and a mapState function:
- The selector may return any value as a result, not just an object.
- The selector normally should return just a single value, and not an object. If you do return an object or an array, be sure to use a memoized selector to avoid unnecessary re-renders.
- The selector function does not receive an
ownPropsargument. However, props can be used through closure (see the examples above) or by using a curried selector. - You can use the
equalityFnoption to customize the comparison behavior
`useDispatch()`
import type { Dispatch } from 'redux'
const dispatch: Dispatch = useDispatch()This hook returns a reference to the dispatch function from the Redux store. You may use it to dispatch actions as needed.
Examples
import React from 'react'
import { useDispatch } from 'react-redux'
export const CounterComponent = ({ value }) => {
const dispatch = useDispatch()
return (
{value}
dispatch({ type: 'increment-counter' })}>
Increment counter
)
}When passing a callback using dispatch to a child component, you may sometimes want to memoize it with useCallback. If the child component is trying to optimize render behavior using React.memo() or similar, this avoids unnecessary rendering of child components due to the changed callback reference.
import React, { useCallback } from 'react'
import { useDispatch } from 'react-redux'
export const CounterComponent = ({ value }) => {
const dispatch = useDispatch()
const incrementCounter = useCallback(
() => dispatch({ type: 'increment-counter' }),
[dispatch],
)
return (
{value}
<MyIncrementButton onIncrement={incrementCounter} />
)
}
export const MyIncrementButton = React.memo(({ onIncrement }) => (
Increment counter
)):::info
The dispatch function reference will be stable as long as the same store instance is being passed to the ``.
Normally, that store instance never changes in an application.
However, the React hooks lint rules do not know that dispatch should be stable, and will warn that the dispatch variable
should be added to dependency arrays for useEffect and useCallback. The simplest solution is to do just that:
export const Todos = () => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(fetchTodos())
// highlight-start
// Safe to add dispatch to the dependencies array
}, [dispatch])
// highlight-end
}:::
`useStore()`
import type { Store } from 'redux'
const store: Store = useStore()This hook returns a reference to the same Redux store that was passed in to the `` component.
This hook should probably not be used frequently. Prefer useSelector() as your primary choice. However, this may be useful for less common scenarios that do require access to the store, such as replacing reducers.
Examples
import React from 'react'
import { useStore } from 'react-redux'
export const ExampleComponent = ({ value }) => {
const store = useStore()
const onClick = () => {
// Not _recommended_, but safe
// This avoids subscribing to the state via `useSelector`
// Prefer moving this logic into a thunk instead
const numTodos = store.getState().todos.length
}
// EXAMPLE ONLY! Do not do this in a real app.
// The component will not automatically update if the store state changes
return {store.getState().todos.length}
}Custom context
The `` component allows you to specify an alternate context via the context prop. This is useful if you're building a complex reusable component, and you don't want your store to collide with any Redux store your consumers' applications might use.
To access an alternate context via the hooks API, use the hook creator functions:
import React from 'react'
import {
Provider,
createStoreHook,
createDispatchHook,
createSelectorHook,
} from 'react-redux'
const MyContext = React.createContext(null)
// Export your custom hooks if you wish to use them in other files.
export const useStore = createStoreHook(MyContext)
export const useDispatch = createDispatchHook(MyContext)
export const useSelector = createSelectorHook(MyContext)
const myStore = createStore(rootReducer)
export function MyProvider({ children }) {
return (
{children}
)
}Usage Warnings
Stale Props and "Zombie Children"
:::info
The React-Redux hooks API has been production-ready since we released it in v7.1.0, and we recommend using the hooks API as the default approach in your components. However, there are a couple of edge cases that can occur, and we're documenting those so that you can be aware of them.
In practice, these are a rare concern - we've received far more comments about these being in the docs than actual reports of these being a real problem in an app.
:::
One of the most difficult aspects of React Redux's implementation is ensuring that if your mapStateToProps function is defined as (state, ownProps), it will be called with the "latest" props every time. Up through version 4, there were recurring bugs reported involving edge case situations, such as errors thrown from a mapState function for a list item whose data had just been deleted.
Starting with version 5, React Redux has attempted to guarantee that consistency with ownProps. In version 7, that is implemented using a custom Subscription class internally in connect(), which forms a nested hierarchy. This ensures that connected components lower in the tree will only receive store update notifications once the nearest connected ancestor has been updated. However, this relies on each connect() instance overriding part of the internal React context, supplying its own unique Subscription instance to form that nesting, and rendering the <ReactReduxContext.Provider> with that new context value.
With hooks, there is no way to render a context provider, which means there's also no nested hierarchy of subscriptions. Because of this, the "stale props" and "zombie child" issues may potentially re-occur in an app that relies on using hooks instead of connect().
Specifically, "stale props" means any case where:
- a selector function relies on this component's props to extract data
- a parent component would re-render and pass down new props as a result of an action
- but this component's selector function executes before this component has had a chance to re-render with those new props
Depending on what props were used and what the current store state is, this may result in incorrect data being returned from the selector, or even an error being thrown.
"Zombie child" refers specifically to the case where:
- Multiple nested connected components are mounted in a first pass, causing a child component to subscribe to the store before its parent
- An action is dispatched that deletes data from the store, such as a todo item
- The parent component would stop rendering that child as a result
- However, because the child subscribed first, its subscription runs before the parent stops rendering it. When it reads a value from the store based on props, that data no longer exists, and if the extraction logic is not careful, this may result in an error being thrown.
useSelector() tries to deal with this by catching all errors that are thrown when the selector is executed due to a store update (but not when it is executed during rendering). When an error occurs, the component will be forced to render, at which point the selector is executed again. This works as long as the selector is a pure function and you do not depend on the selector throwing errors.
If you prefer to deal with this issue yourself, here are some possible options for avoiding these problems altogether with useSelector():
- Don't rely on props in your selector function for extracting data
- In cases where you do rely on props in your selector function and those props may change over time, or the data you're extracting may be based on items that can be deleted, try writing the selector functions defensively. Don't just reach straight into
state.todos[props.id].name- readstate.todos[props.id]first, and verify that it exists before trying to readtodo.name. - Because
connectadds the necessarySubscriptionto the context provider and delays evaluating child subscriptions until the connected component has re-rendered, putting a connected component in the component tree just above the component usinguseSelectorwill prevent these issues as long as the connected component gets re-rendered due to the same store update as the hooks component.
:::info
For a longer description of these scenarios, see:
- "Stale props and zombie children in Redux" by Kai Hao
- A chat log that describes the problems in more detail
- issue #1179
:::
Performance
As mentioned earlier, by default useSelector() will do a reference equality comparison of the selected value when running the selector function after an action is dispatched, and will only cause the component to re-render if the selected value changed. However, unlike connect(), useSelector() does not prevent the component from re-rendering due to its parent re-rendering, even if the component's props did not change.
If further performance optimizations are necessary, you may consider wrapping your function component in React.memo():
const CounterComponent = ({ name }) => {
const counter = useSelector((state) => state.counter)
return (
{name}: {counter}
)
}
export const MemoizedCounterComponent = React.memo(CounterComponent)Hooks Recipes
We've pared down our hooks API from the original alpha release, focusing on a more minimal set of API primitives.
However, you may still wish to use some of the approaches we tried in your own apps. These examples should be ready
to copy and paste into your own codebase.
Recipe: `useActions()`
This hook was in our original alpha release, but removed in v7.1.0-alpha.4, based on Dan Abramov's suggestion.
That suggestion was based on "binding action creators" not being as useful in a hooks-based use case, and causing too
much conceptual overhead and syntactic complexity.
You should probably prefer to call the useDispatch hook in your components to retrieve a reference to dispatch,
and manually call dispatch(someActionCreator()) in callbacks and effects as needed. You may also use the ReduxbindActionCreators function in your own code to bind action creators,
or "manually" bind them like const boundAddTodo = (text) => dispatch(addTodo(text)).
However, if you'd like to still use this hook yourself, here's a copy-pastable version that supports passing in action
creators as a single function, an array, or an object.
import { bindActionCreators } from 'redux'
import { useDispatch } from 'react-redux'
import { useMemo } from 'react'
export function useActions(actions, deps) {
const dispatch = useDispatch()
return useMemo(
() => {
if (Array.isArray(actions)) {
return actions.map((a) => bindActionCreators(a, dispatch))
}
return bindActionCreators(actions, dispatch)
},
deps ? [dispatch, ...deps] : [dispatch],
)
}Recipe: `useShallowEqualSelector()`
import { useSelector, shallowEqual } from 'react-redux'
export function useShallowEqualSelector(selector) {
return useSelector(selector, shallowEqual)
}Additional considerations when using hooks
There are some architectural trade-offs to take into consideration when deciding whether to use hooks or not. Mark Erikson summarizes these nicely in his two blog posts Thoughts on React Hooks, Redux, and Separation of Concerns and Hooks, HOCs, and Tradeoffs.
File: docs/api/Provider.md
id: provider title: Provider sidebar_label: Provider hide_title: true
`Provider`
Overview
The `` component makes the Redux store available to any nested components that need to access the Redux store.
Since any React component in a React Redux app can be connected to the store, most applications will render a `` at the top level, with the entire appβs component tree inside of it.
The Hooks and connect APIs can then access the provided store instance via React's Context mechanism.
Props
interface ProviderProps<A extends Action<string> = UnknownAction, S = unknown> {
/**
* The single Redux store in your application.
*/
store: Store<S, A>
/**
* An optional server state snapshot. Will be used during initial hydration render
* if available, to ensure that the UI output is consistent with the HTML generated on the server.
* New in 8.0
*/
serverState?: S
/**
* Optional context to be used internally in react-redux. Use React.createContext()
* to create a context to be used.
* If this is used, you'll need to customize `connect` by supplying the same
* context provided to the Provider.
* Set the initial value to null, and the hooks will error
* if this is not overwritten by Provider.
*/
context?: Context<ReactReduxContextValue<S, A> | null>
/** Global configuration for the `useSelector` stability check */
stabilityCheck?: DevModeCheckFrequency
/** Global configuration for the `useSelector` identity function check */
identityFunctionCheck?: DevModeCheckFrequency
/** The top-level React elements in your component tree, such as `<App />` **/
children: ReactNode
}Typically, you only need to pass ``.
You may provide a context instance. If you do so, you will need to provide the same context instance to all of your connected components as well. Failure to provide the correct context results in this runtime error:
Invariant Violation
Could not find "store" in the context of "Connect(MyComponent)". Either wrap the root component in a
, or pass a custom React context provider toand the corresponding React context consumer to Connect(Todo) in connect options.
React 18 SSR Usage
As of React-Redux v8, `` now accepts a serverState prop for use in SSR hydration scenarios. This is necessary if you are calling hydrateRoot in order to avoid hydration mismatches.
You should pass the entire serialized state from the server as the serverState prop, and React will use this state for the initial hydration render. After that, it will apply any updates from changes that occurred on the client during the setup process.
Examples
Basic Usage
In the example below, the <App /> component is our root-level component. This means itβs at the very top of our component hierarchy.
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import { App } from './App'
import createStore from './createReduxStore'
const store = createStore()
// As of React 18
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<App />
,
)React 18 SSR Hydration
In this example, the client has received HTML rendered by the server, as well as a serialized Redux state attached to window. The serialized state is used to both pre-fill the store's contents, and passed as the serverState prop to ``
import { hydrateRoot } from 'react-dom/client'
import { configureStore } from '@reduxjs/toolkit'
import { Provider } from 'react-redux'
const preloadedState = window.__PRELOADED_STATE__
const clientStore = configureStore({
reducer: rootReducer,
preloadedState,
})
hydrateRoot(
document.getElementById('root'),
<App />
,
)File: docs/tutorials/connect.md
id: connect title: 'Tutorial: Connect API' hide_title: true sidebar_label: 'Tutorial: Connect API'
Tutorial: Using the `connect` API
:::tip
We now recommend using the React-Redux hooks API as the default. However, the connect API still works fine.
This tutorial also shows some older practices we no longer recommend, like separating Redux logic into folders by type. We've kept this tutorial as-is for completeness, but recommend reading through the "Redux Essentials" tutorial and the Redux Style Guide in the Redux docs for our current best practices.
We're working on a new tutorial that will introduce the hooks APIs. Until then, we suggest reading Redux Fundamentals, Part 5: UI and React for a hooks tutorial.
:::
To see how to use React Redux in practice, weβll show a step-by-step example by creating a todo list app.
A Todo List Example
Jump to
- π€ Just show me the code
- π Providing the store
- βοΈ Connecting the Component
The React UI Components
We have implemented our React UI components as follows:
TodoAppis the entry component for our app. It renders the header, theAddTodo,TodoList, andVisibilityFilterscomponents.AddTodois the component that allows a user to input a todo item and add to the list upon clicking its βAdd Todoβ button:- It uses a controlled input that sets state upon
onChange. - When the user clicks on the βAdd Todoβ button, it dispatches the action (that we will provide using React Redux) to add the todo to the store.
- It uses a controlled input that sets state upon
TodoListis the component that renders the list of todos:- It renders the filtered list of todos when one of the
VisibilityFiltersis selected.
- It renders the filtered list of todos when one of the
Todois the component that renders a single todo item:- It renders the todo content, and shows that a todo is completed by crossing it out.
- It dispatches the action to toggle the todo's complete status upon
onClick.
VisibilityFiltersrenders a simple set of filters: all, completed, and incomplete. Clicking on each one of them filters the todos:- It accepts an
activeFilterprop from the parent that indicates which filter is currently selected by the user. An active filter is rendered with an underscore. - It dispatches the
setFilteraction to update the selected filter.
- It accepts an
constantsholds the constants data for our app.- And finally
indexrenders our app to the DOM.
The Redux Store
The Redux portion of the application has been set up using the patterns recommended in the Redux docs:
- Store
todos: A normalized reducer of todos. It contains abyIdsmap of all todos and aallIdsthat contains the list of all ids.visibilityFilters: A simple stringall,completed, orincomplete.
- Action Creators
addTodocreates the action to add todos. It takes a single string variablecontentand returns anADD_TODOaction withpayloadcontaining a self-incrementedidandcontenttoggleTodocreates the action to toggle todos. It takes a single number variableidand returns aTOGGLE_TODOaction withpayloadcontainingidonlysetFiltercreates the action to set the appβs active filter. It takes a single string variablefilterand returns aSET_FILTERaction withpayloadcontaining thefilteritself
- Reducers
- The
todosreducer- Appends the
idto itsallIdsfield and sets the todo within itsbyIdsfield upon receiving theADD_TODOaction - Toggles the
completedfield for the todo upon receiving theTOGGLE_TODOaction
- Appends the
- The
visibilityFiltersreducer sets its slice of store to the new filter it receives from theSET_FILTERaction payload
- The
- Action Types
- We use a file
actionTypes.jsto hold the constants of action types to be reused
- We use a file
- Selectors
getTodoListreturns theallIdslist from thetodosstoregetTodoByIdfinds the todo in the store given byidgetTodosis slightly more complex. It takes all theids fromallIds, finds each todo inbyIds, and returns the final array of todosgetTodosByVisibilityFilterfilters the todos according to the visibility filter
You may check out this CodeSandbox for the source code of the UI components and the unconnected Redux store described above.
We will now show how to connect this store to our app using React Redux.
Providing the Store
First we need to make the store available to our app. To do this, we wrap our app with the `` API provided by React Redux.
// index.js
import React from 'react'
import ReactDOM from 'react-dom'
import TodoApp from './TodoApp'
import { Provider } from 'react-redux'
import store from './redux/store'
// As of React 18
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<TodoApp />
,
)Notice how our <TodoApp /> is now wrapped with the `` with store passed in as a prop.
Connecting the Components
React Redux provides a connect function for you to read values from the Redux store (and re-read the values when the store updates).
The connect function takes two arguments, both optional:
mapStateToProps: called every time the store state changes. It receives the entire store state, and should return an object of data this component needs.mapDispatchToProps: this parameter can either be a function, or an object.- If itβs a function, it will be called once on component creation. It will receive
dispatchas an argument, and should return an object full of functions that usedispatchto dispatch actions. - If itβs an object full of action creators, each action creator will be turned into a prop function that automatically dispatches its action when called. Note: We recommend using this βobject shorthandβ form.
- If itβs a function, it will be called once on component creation. It will receive
Normally, youβll call connect in this way:
const mapStateToProps = (state, ownProps) => ({
// ... computed data from state and optionally ownProps
})
const mapDispatchToProps = {
// ... normally is an object full of action creators
}
// `connect` returns a new function that accepts the component to wrap:
const connectToStore = connect(mapStateToProps, mapDispatchToProps)
// and that function returns the connected, wrapper component:
const ConnectedComponent = connectToStore(Component)
// We normally do both in one step, like this:
connect(mapStateToProps, mapDispatchToProps)(Component)Letβs work on <AddTodo /> first. It needs to trigger changes to the store to add new todos. Therefore, it needs to be able to dispatch actions to the store. Hereβs how we do it.
Our addTodo action creator looks like this:
// redux/actions.js
import { ADD_TODO } from './actionTypes'
let nextTodoId = 0
export const addTodo = (content) => ({
type: ADD_TODO,
payload: {
id: ++nextTodoId,
content,
},
})
// ... other actionsBy passing it to connect, our component receives it as a prop, and it will automatically dispatch the action when itβs called.
// components/AddTodo.js
// ... other imports
import { connect } from 'react-redux'
import { addTodo } from '../redux/actions'
class AddTodo extends React.Component {
// ... component implementation
}
export default connect(null, { addTodo })(AddTodo)Notice now that <AddTodo /> is wrapped with a parent component called <Connect(AddTodo) />. Meanwhile, <AddTodo /> now gains one prop: the addTodo action.
We also need to implement the handleAddTodo function to let it dispatch the addTodo action and reset the input
// components/AddTodo.js
import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../redux/actions'
class AddTodo extends React.Component {
// ...
handleAddTodo = () => {
// dispatches actions to add todo
this.props.addTodo(this.state.input)
// sets state back to empty string
this.setState({ input: '' })
}
render() {
return (
this.updateInput(e.target.value)}
value={this.state.input}
/>
Add Todo
)
}
}
export default connect(null, { addTodo })(AddTodo)Now our <AddTodo /> 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:
You should also see that the store has changed accordingly:
The <TodoList /> component is responsible for rendering the list of todos. Therefore, it needs to read data from the store. We enable it by calling connect with the mapStateToProps parameter, a function describing which part of the data we need from the store.
Our <Todo /> component takes the todo item as props. We have this information from the byIds field of the todos. However, we also need the information from the allIds field of the store indicating which todos and in what order they should be rendered. Our mapStateToProps function may look like this:
// components/TodoList.js
// ...other imports
import { connect } from "react-redux";
const TodoList = // ... UI component implementation
const mapStateToProps = state => {
const { byIds, allIds } = state.todos || {};
const todos =
allIds && allIds.length
? allIds.map(id => (byIds ? { ...byIds[id], id } : null))
: null;
return { todos };
};
export default connect(mapStateToProps)(TodoList);Luckily we have a selector that does exactly this. We may simply import the selector and use it here.
// redux/selectors.js
export const getTodosState = (store) => store.todos
export const getTodoList = (store) =>
getTodosState(store) ? getTodosState(store).allIds : []
export const getTodoById = (store, id) =>
getTodosState(store) ? { ...getTodosState(store).byIds[id], id } : {}
export const getTodos = (store) =>
getTodoList(store).map((id) => getTodoById(store, id))// components/TodoList.js
// ...other imports
import { connect } from "react-redux";
import { getTodos } from "../redux/selectors";
const TodoList = // ... UI component implementation
export default connect(state => ({ todos: getTodos(state) }))(TodoList);We recommend encapsulating any complex lookups or computations of data in selector functions. In addition, you can further optimize the performance by using Reselect to write βmemoizedβ selectors that can skip unnecessary work. (See the Redux docs page on Computing Derived Data and the blog post Idiomatic Redux: Using Reselect Selectors for Encapsulation and Performance for more information on why and how to use selector functions.)
Now that our <TodoList /> is connected to the store. It should receive the list of todos, map over them, and pass each todo to the <Todo /> component. <Todo /> will in turn render them to the screen. Now try adding a todo. It should come up on our todo list!
We will connect more components. Before we do this, letβs pause and learn a bit more about connect first.
Common ways of calling `connect`
Depending on what kind of components you are working with, there are different ways of calling connect , with the most common ones summarized as below:
| Do Not Subscribe to the Store | Subscribe to the Store | |
|---|---|---|
| Do Not Inject Action Creators | connect()(Component) |
connect(mapStateToProps)(Component) |
| Inject Action Creators | connect(null, mapDispatchToProps)(Component) |
connect(mapStateToProps, mapDispatchToProps)(Component) |
Do not subscribe to the store and do not inject action creators
If you call connect without providing any arguments, your component will:
- not re-render when the store changes
- receive
props.dispatchthat you may use to manually dispatch action
// ... Component
export default connect()(Component) // Component will receive `dispatch` (just like our <TodoList />!)Subscribe to the store and do not inject action creators
If you call connect with only mapStateToProps, your component will:
- subscribe to the values that
mapStateToPropsextracts from the store, and re-render only when those values have changed - receive
props.dispatchthat you may use to manually dispatch action
// ... Component
const mapStateToProps = (state) => state.partOfState
export default connect(mapStateToProps)(Component)Do not subscribe to the store and inject action creators
If you call connect with only mapDispatchToProps, your component will:
- not re-render when the store changes
- receive each of the action creators you inject with
mapDispatchToPropsas props and automatically dispatch the actions upon being called
import { addTodo } from './actionCreators'
// ... Component
export default connect(null, { addTodo })(Component)Subscribe to the store and inject action creators
If you call connect with both mapStateToProps and mapDispatchToProps, your component will:
- subscribe to the values that
mapStateToPropsextracts from the store, and re-render only when those values have changed - receive all of the action creators you inject with
mapDispatchToPropsas props and automatically dispatch the actions upon being called.
import * as actionCreators from './actionCreators'
// ... Component
const mapStateToProps = (state) => state.partOfState
export default connect(mapStateToProps, actionCreators)(Component)These four cases cover the most basic usages of connect. To read more about connect, continue reading our API section that explains it in more detail.
Now letβs connect the rest of our <TodoApp />.
How should we implement the interaction of toggling todos? A keen reader might already have an answer. If you have your environment set up and have followed through up until this point, now is a good time to leave it aside and implement the feature by yourself. There would be no surprise that we connect our <Todo /> to dispatch toggleTodo in a similar way:
// components/Todo.js
// ... other imports
import { connect } from "react-redux";
import { toggleTodo } from "../redux/actions";
const Todo = // ... component implementation
export default connect(
null,
{ toggleTodo }
)(Todo);Now our todoβs can be toggled complete. Weβre almost there!
Finally, letβs implement our VisibilityFilters feature.
The <VisibilityFilters /> component needs to be able to read from the store which filter is currently active, and dispatch actions to the store. Therefore, we need to pass both a mapStateToProps and mapDispatchToProps. The mapStateToProps here can be a simple accessor of the visibilityFilter state. And the mapDispatchToProps will contain the setFilter action creator.
// components/VisibilityFilters.js
// ... other imports
import { connect } from "react-redux";
import { setFilter } from "../redux/actions";
const VisibilityFilters = // ... component implementation
const mapStateToProps = state => {
return { activeFilter: state.visibilityFilter };
};
export default connect(
mapStateToProps,
{ setFilter }
)(VisibilityFilters);Meanwhile, we also need to update our <TodoList /> component to filter todos according to the active filter. Previously the mapStateToProps we passed to the <TodoList /> connect function call was simply the selector that selects the whole list of todos. Letβs write another selector to help filtering todos by their status.
// redux/selectors.js
// ... other selectors
export const getTodosByVisibilityFilter = (store, visibilityFilter) => {
const allTodos = getTodos(store)
switch (visibilityFilter) {
case VISIBILITY_FILTERS.COMPLETED:
return allTodos.filter((todo) => todo.completed)
case VISIBILITY_FILTERS.INCOMPLETE:
return allTodos.filter((todo) => !todo.completed)
case VISIBILITY_FILTERS.ALL:
default:
return allTodos
}
}And connecting to the store with the help of the selector:
// components/TodoList.js
// ...
const mapStateToProps = (state) => {
const { visibilityFilter } = state
const todos = getTodosByVisibilityFilter(state, visibilityFilter)
return { todos }
}
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? ππ
Links
- Usage with React
- Using the React Redux Bindings
- Higher Order Components in Depth
- Computing Derived Data
- Idiomatic Redux: Using Reselect Selectors for Encapsulation and Performance
Get More Help
- Reactiflux Redux channel
- StackOverflow
- GitHub Issues
File: docs/tutorials/quick-start.md
id: quick-start title: Quick Start sidebar_label: Quick Start hide_title: true
React Redux Quick Start
:::tip What You'll Learn
- How to set up and use Redux Toolkit with React Redux
:::
:::info Prerequisites
- Familiarity with ES6 syntax and features
- Knowledge of React terminology: JSX, State, Function Components, Props, and Hooks
- Understanding of Redux terms and concepts
:::
Introduction
Welcome to the React Redux Quick Start tutorial! This tutorial will briefly introduce you to React Redux and teach you how to start using it correctly.
How to Read This Tutorial
This page will focus on just how to set up a Redux application with Redux Toolkit and the main APIs you'll use. For explanations of what Redux is, how it works, and full examples of how to use Redux Toolkit, see the Redux core docs tutorials.
For this tutorial, we assume that you're using Redux Toolkit and React Redux together, as that is the standard Redux usage pattern. The examples are based on a typical Create-React-App folder structure where all the application code is in a src, but the patterns can be adapted to whatever project or folder setup you're using.
The Redux+JS template for Create-React-App comes with this same project setup already configured.
Usage Summary
Install Redux Toolkit and React Redux
Add the Redux Toolkit and React Redux packages to your project:
npm install @reduxjs/toolkit react-reduxCreate a Redux Store
Create a file named src/app/store.js. Import the configureStore API from Redux Toolkit. We'll start by creating an empty Redux store, and exporting it:
import { configureStore } from '@reduxjs/toolkit'
export default configureStore({
reducer: {},
})This creates a Redux store, and also automatically configure the Redux DevTools extension so that you can inspect the store while developing.
Provide the Redux Store to React
Once the store is created, we can make it available to our React components by putting a React Redux around our application in `src/index.js`. Import the Redux store we just created, put a around your <App>, and pass the store as a prop:
import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import App from './App'
// highlight-start
import store from './app/store'
import { Provider } from 'react-redux'
// highlight-end
// As of React 18
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
// highlight-next-line
<App />
,
)Create a Redux State Slice
Add a new file named src/features/counter/counterSlice.js. In that file, import the createSlice API from Redux Toolkit.
Creating a slice requires a string name to identify the slice, an initial state value, and one or more reducer functions to define how the state can be updated. Once a slice is created, we can export the generated Redux action creators and the reducer function for the whole slice.
Redux requires that we write all state updates immutably, by making copies of data and updating the copies. However, Redux Toolkit's createSlice and createReducer APIs use Immer inside to allow us to write "mutating" update logic that becomes correct immutable updates.
import { createSlice } from '@reduxjs/toolkit'
export const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0,
},
reducers: {
increment: (state) => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
// doesn't actually mutate the state because it uses the Immer library,
// which detects changes to a "draft state" and produces a brand new
// immutable state based off those changes.
// Also, no return statement is required from these functions.
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
incrementByAmount: (state, action) => {
state.value += action.payload
},
},
})
// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducerAdd Slice Reducers to the Store
Next, we need to import the reducer function from the counter slice and add it to our store. By defining a field inside the reducer parameter, we tell the store to use this slice reducer function to handle all updates to that state.
import { configureStore } from '@reduxjs/toolkit'
// highlight-next-line
import counterReducer from '../features/counter/counterSlice'
export default configureStore({
reducer: {
// highlight-next-line
counter: counterReducer,
},
})Use Redux State and Actions in React Components
Now we can use the React Redux hooks to let React components interact with the Redux store. We can read data from the store with useSelector, and dispatch actions using useDispatch. Create a src/features/counter/Counter.js file with a <Counter> component inside, then import that component into App.js and render it inside of <App>.
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { decrement, increment } from './counterSlice'
import styles from './Counter.module.css'
export function Counter() {
const count = useSelector((state) => state.counter.value)
const dispatch = useDispatch()
return (
dispatch(increment())}
>
Increment
{count}
dispatch(decrement())}
>
Decrement
)
}Now, any time you click the "Increment" and "Decrement buttons:
- The corresponding Redux action will be dispatched to the store
- The counter slice reducer will see the actions and update its state
- The
<Counter>component will see the new state value from the store and re-render itself with the new data
What You've Learned
That was a brief overview of how to set up and use Redux Toolkit with React. Recapping the details:
:::tip Summary
- Create a Redux store with
configureStoreconfigureStoreaccepts areducerfunction as a named argumentconfigureStoreautomatically sets up the store with good default settings
- Provide the Redux store to the React application components
- Put a React Redux `` component around your
<App /> - Pass the Redux store as ``
- Put a React Redux `` component around your
- Create a Redux "slice" reducer with
createSlice- Call
createSlicewith a string name, an initial state, and named reducer functions - Reducer functions may "mutate" the state using Immer
- Export the generated slice reducer and action creators
- Call
- Use the React Redux
useSelector/useDispatchhooks in React components- Read data from the store with the
useSelectorhook - Get the
dispatchfunction with theuseDispatchhook, and dispatch actions as needed
- Read data from the store with the
:::
Full Counter App Example
Here's the complete Counter application as a running CodeSandbox:
What's Next?
We recommend going through the "Redux Essentials" and "Redux Fundamentals" tutorials in the Redux core docs, which will give you a complete understanding of how Redux works, what Redux Toolkit and React Redux do, and how to use it correctly.
File: docs/tutorials/typescript.md
id: typescript-quick-start title: TypeScript Quick Start sidebar_label: TypeScript Quick Start hide_title: true
React Redux TypeScript Quick Start
:::tip What You'll Learn
- How to set up and use Redux Toolkit and React Redux with TypeScript
:::
:::info Prerequisites
- Knowledge of React Hooks
- Understanding of Redux terms and concepts
- Understanding of TypeScript syntax and concepts
:::
Introduction
Welcome to the React Redux TypeScript Quick Start tutorial! This tutorial will briefly show how to use TypeScript with Redux Toolkit and React-Redux.
This page focuses on just how to set up the TypeScript aspects. For explanations of what Redux is, how it works, and full examples of how to use Redux, see the Redux core docs tutorials.
React Redux is also written in TypeScript as of version 8, and also includes its own type definitions.
The Redux+TS template for Create-React-App comes with a working example of these patterns already configured.
:::info
The recently updated @types/react@18 major version has changed component definitions to remove having children as a prop by default. This causes errors if you have multiple copies of @types/react in your project. To fix this, tell your package manager to resolve @types/react to a single version. Details:
https://github.com/facebook/react/issues/24304#issuecomment-1094565891
:::
Project Setup
Define Root State and Dispatch Types
Redux Toolkit's configureStore API should not need any additional typings. You will, however, want to extract the RootState type and the Dispatch type so that they can be referenced as needed. Inferring these types from the store itself means that they correctly update as you add more state slices or modify middleware settings.
Since those are types, it's safe to export them directly from your store setup file such as app/store.ts and import them directly into other files.
import { configureStore } from '@reduxjs/toolkit'
// ...
const store = configureStore({
reducer: {
posts: postsReducer,
comments: commentsReducer,
users: usersReducer,
},
})
// highlight-start
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch
// highlight-endDefine Typed Hooks
While it's possible to import the RootState and AppDispatch types into each component, it's better to create typed versions of the useDispatch and useSelector hooks for usage in your application. This is important for a couple reasons:
- For
useSelector, it saves you the need to type(state: RootState)every time - For
useDispatch, the defaultDispatchtype does not know about thunks. In order to correctly dispatch thunks, you need to use the specific customizedAppDispatchtype from the store that includes the thunk middleware types, and use that withuseDispatch. Adding a pre-typeduseDispatchhook keeps you from forgetting to importAppDispatchwhere it's needed.
Since these are actual variables, not types, it's important to define them in a separate file such as app/hooks.ts, not the store setup file. This allows you to import them into any component file that needs to use the hooks, and avoids potential circular import dependency issues.
import { useDispatch, useSelector } from 'react-redux'
import type { RootState, AppDispatch } from './store'
// highlight-start
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = useDispatch.withTypes<AppDispatch>()
export const useAppSelector = useSelector.withTypes<RootState>()
// highlight-endApplication Usage
Define Slice State and Action Types
Each slice file should define a type for its initial state value, so that createSlice can correctly infer the type of state in each case reducer.
All generated actions should be defined using the PayloadAction<T> type from Redux Toolkit, which takes the type of the action.payload field as its generic argument.
You can safely import the RootState type from the store file here. It's a circular import, but the TypeScript compiler can correctly handle that for types. This may be needed for use cases like writing selector functions.
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import type { RootState } from '../../app/store'
// highlight-start
// Define a type for the slice state
interface CounterState {
value: number
}
// Define the initial state using that type
const initialState: CounterState = {
value: 0,
}
// highlight-end
export const counterSlice = createSlice({
name: 'counter',
// `createSlice` will infer the state type from the `initialState` argument
initialState,
reducers: {
increment: (state) => {
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
// highlight-start
// Use the PayloadAction type to declare the contents of `action.payload`
incrementByAmount: (state, action: PayloadAction<number>) => {
// highlight-end
state.value += action.payload
},
},
})
export const { increment, decrement, incrementByAmount } = counterSlice.actions
// Other code such as selectors can use the imported `RootState` type
export const selectCount = (state: RootState) => state.counter.value
export default counterSlice.reducerThe generated action creators will be correctly typed to accept a payload argument based on the PayloadAction<T> type you provided for the reducer. For example, incrementByAmount requires a number as its argument.
In some cases, TypeScript may unnecessarily tighten the type of the initial state. If that happens, you can work around it by casting the initial state using as, instead of declaring the type of the variable:
// Workaround: cast state instead of declaring variable type
const initialState = {
value: 0,
} as CounterStateUse Typed Hooks in Components
In component files, import the pre-typed hooks instead of the standard hooks from React-Redux.
import React, { useState } from 'react'
// highlight-next-line
import { useAppSelector, useAppDispatch } from 'app/hooks'
import { decrement, increment } from './counterSlice'
export function Counter() {
// highlight-start
// The `state` arg is correctly typed as `RootState` already
const count = useAppSelector((state) => state.counter.value)
const dispatch = useAppDispatch()
// highlight-end
// omit rendering logic
}What's Next?
See the "Usage with TypeScript" page for extended details on how to use Redux Toolkit's APIs with TypeScript.