Skip to content

Commit ea4e8e4

Browse files
committed
Merge branch 'master' of https://github.com/reduxjs/redux-toolkit into expo-example-ci
2 parents 56f6b4c + 4afef4a commit ea4e8e4

File tree

9 files changed

+1525
-19
lines changed

9 files changed

+1525
-19
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React from 'react'
2+
3+
export const DetailedExplanation = ({
4+
children,
5+
title = 'Detailed Explanation'
6+
}) => {
7+
return (
8+
<details className="detailed-explanation">
9+
<summary>
10+
<h4>{title}</h4>
11+
</summary>
12+
{children}
13+
</details>
14+
)
15+
}
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
---
2+
id: why-rtk-is-redux-today
3+
title: Why Redux Toolkit is How To Use Redux Today
4+
# Yes, we are serious with the title. It's okay as it is. Please don't open more Pull Requests to change it.
5+
description: 'Introduction > Why RTK is Redux Today: details on how RTK replaces the Redux core'
6+
---
7+
8+
## What is Redux Toolkit?
9+
10+
[**Redux Toolkit**](https://redux-toolkit.js.org) (also known as **"RTK"** for short) is our official recommended approach for writing Redux logic. The `@reduxjs/toolkit` package wraps around the core `redux` package, and contains API methods and common dependencies that we think are essential for building a Redux app. Redux Toolkit builds in our suggested best practices, simplifies most Redux tasks, prevents common mistakes, and makes it easier to write Redux applications.
11+
12+
**If you are writing _any_ Redux logic today, you _should_ be using Redux Toolkit to write that code!**
13+
14+
RTK includes utilities that help simplify many common use cases, including [store setup](https://redux-toolkit.js.org/api/configureStore),
15+
[creating reducers and writing immutable update logic](https://redux-toolkit.js.org/api/createreducer),
16+
and even [creating entire "slices" of state at once](https://redux-toolkit.js.org/api/createslice).
17+
18+
Whether you're a brand new Redux user setting up your first project, or an experienced user who wants to
19+
simplify an existing application, **[Redux Toolkit](https://redux-toolkit.js.org/)** can help you
20+
make your Redux code better.
21+
22+
:::tip
23+
24+
See these pages to learn how to use "modern Redux" with Redux Toolkit:
25+
26+
- [**The "Redux Essentials" tutorial**](https://redux.js.org/tutorials/essentials/part-1-overview-concepts), which teaches "how to use Redux, the right way" with Redux Toolkit for real-world apps,
27+
- [**Redux Fundamentals, Part 8: Modern Redux with Redux Toolkit**](https://redux.js.org/tutorials/fundamentals/part-8-modern-redux), which shows how to convert the low-level examples from earlier sections of the tutorial into modern Redux Toolkit equivalents
28+
- [**Using Redux: Migrating to Modern Redux**](../usage/migrating-to-modern-redux.mdx), which covers how to migrate different kinds of legacy Redux logic into modern Redux equivalents
29+
30+
:::
31+
32+
## How Redux Toolkit Is Different Than the Redux Core
33+
34+
### What Is "Redux"?
35+
36+
The first thing to ask is, "what is Redux?"
37+
38+
Redux is really:
39+
40+
- A single store containing "global" state
41+
- Dispatching plain object actions to the store when something happens in the app
42+
- Pure reducer functions looking at those actions and returning immutably updated state
43+
44+
While it's not required, [your Redux code also normally includes](https://redux.js.org/tutorials/fundamentals/part-7-standard-patterns):
45+
46+
- Action creators that generate those action objects
47+
- Middleware to enable side effects
48+
- Thunk functions that contain sync or async logic with side effects
49+
- Normalized state to enable looking up items by ID
50+
- Memoized selector functions with the Reselect library for optimizing derived data
51+
- The Redux DevTools Extension to view your action history and state changes
52+
- TypeScript types for actions, state, and other functions
53+
54+
Additionally, Redux is normally used with the React-Redux library to let your React components talk to a Redux store.
55+
56+
### What Does the Redux Core Do?
57+
58+
The Redux core is a very small and deliberately unopinionated library. It provides a few small API primitives:
59+
60+
- `createStore` to actually create a Redux store
61+
- `combineReducers` to combine multiple slice reducers into a single larger reducer
62+
- `applyMiddleware` to combine multiple middleware into a store enhancer
63+
- `compose` to combine multiple store enhancers into a single store enhancer
64+
65+
Other than that, all the other Redux-related logic in your app has to be written entirely by you.
66+
67+
The good news is that this means Redux _can_ be used in many different ways. The bad news is that there are no helpers to make any of your code easier to write.
68+
69+
For example, a reducer function is _just_ a function. Prior to Redux Toolkit, you'd typically write that reducer with a `switch` statement and manual updates. You'd also probably have hand-written action creators and action type constants along with it:
70+
71+
```js title="Legacy hand-written Redux usage"
72+
const ADD_TODO = 'ADD_TODO'
73+
const TODO_TOGGLED = 'TODO_TOGGLED'
74+
75+
export const addTodo = (text) => ({
76+
type: ADD_TODO,
77+
payload: { text, id: nanoid() },
78+
})
79+
80+
export const todoToggled = (id) => ({
81+
type: TODO_TOGGLED,
82+
payload: { id },
83+
})
84+
85+
export const todosReducer = (state = [], action) => {
86+
switch (action.type) {
87+
case ADD_TODO:
88+
return state.concat({
89+
id: action.payload.id,
90+
text: action.payload.text,
91+
completed: false,
92+
})
93+
case TODO_TOGGLED:
94+
return state.map((todo) => {
95+
if (todo.id !== action.payload.id) return todo
96+
97+
return {
98+
...todo,
99+
completed: !todo.completed,
100+
}
101+
})
102+
default:
103+
return state
104+
}
105+
}
106+
```
107+
108+
None of this code specifically depends on any API from the `redux` core library. But, this is a lot of code to write. Immutable updates required a lot of hand-written object spreads and array operations, and it was very easy to make mistakes and accidentally mutate state in the process (always the #1 cause of Redux bugs!). It was also common, though not strictly required, to spread the code for one feature across multiple files like `actions/todos.js`, `constants/todos.js`, and `reducers/todos.js`.
109+
110+
Additionally, store setup usually required a series of steps to add commonly used middleware like thunks and enable Redux DevTools Extension support, even though these are standard tools used in almost every Redux app.
111+
112+
### What Does Redux Toolkit Do?
113+
114+
While these _were_ the patterns originally shown in the Redux docs, they unfortunately require a lot of very verbose and repetitive code. Most of this boilerplate isn't _necessary_ to use Redux. On top of that, the boilerplate-y code lead to more opportunities to make mistakes.
115+
116+
**We specifically created Redux Toolkit to eliminate the "boilerplate" from hand-written Redux logic, prevent common mistakes, and provide APIs that simplify standard Redux tasks**.
117+
118+
Redux Toolkit starts with two key APIs that simplify the most common things you do in every Redux app:
119+
120+
- `configureStore` sets up a well-configured Redux store with a single function call, including combining reducers, adding the thunk middleware, and setting up the Redux DevTools integration. It also is easier to configure than `createStore`, because it takes named options parameters.
121+
- `createSlice` lets you write reducers that use [the Immer library](https://immerjs.github.io/immer/) to enable writing immutable updates using "mutating" JS syntax like `state.value = 123`, with no spreads needed. It also automatically generates action creator functions for each reducer, and generates action type strings internally based on your reducer's names. Finally, it works great with TypeScript.
122+
123+
That means that the code _you_ write can be drastically simpler. For example, that same todos reducer could just be:
124+
125+
```js title="features/todos/todosSlice.js"
126+
import { createSlice } from '@reduxjs/toolkit'
127+
128+
const todosSlice = createSlice({
129+
name: 'todos',
130+
initialState: [],
131+
reducers: {
132+
todoAdded(state, action) {
133+
state.push({
134+
id: action.payload.id,
135+
text: action.payload.text,
136+
completed: false,
137+
})
138+
},
139+
todoToggled(state, action) {
140+
const todo = state.find((todo) => todo.id === action.payload)
141+
todo.completed = !todo.completed
142+
},
143+
},
144+
})
145+
146+
export const { todoAdded, todoToggled } = todosSlice.actions
147+
export default todosSlice.reducer
148+
```
149+
150+
All of the action creators and action types are generated automatically, and the reducer code is shorter and easier to understand. It's also much more clear what's actually being updated in each case.
151+
152+
With `configureStore`, the store setup can be simplified down to:
153+
154+
```js title="app/store.js"
155+
import { configureStore } from '@reduxjs/toolkit'
156+
import todosReducer from '../features/todos/todosSlice'
157+
import filtersReducer from '../features/filters/filtersSlice'
158+
159+
export const store = configureStore({
160+
reducer: {
161+
todos: todosReducer,
162+
filters: filtersReducer,
163+
},
164+
})
165+
```
166+
167+
Note that **this one `configureStore` call automatically does all the usual setup work you'd have done manually**:
168+
169+
- The slice reducers were automatically passed to `combineReducers()`
170+
- The `redux-thunk` middleware was automatically added
171+
- Dev-mode middleware was added to catch accidental mutations
172+
- The Redux DevTools Extension was automatically set up
173+
- The middleware and DevTools enhancers were composed together and added to the store
174+
175+
At the same time, **`configureStore` provides the options to let users modify any of those default behaviors** (like turning off thunks and adding sagas, or disabling the DevTools in production),
176+
177+
From there, Redux Toolkit includes other APIs for common Redux tasks:
178+
179+
- `createAsyncThunk`: abstracts the standard "dispatch actions before/after an async request" pattern
180+
- `createEntityAdapter`: prebuilt reducers and selectors for CRUD operations on normalized state
181+
- `createSelector`: a re-export of the standard Reselect API for memoized selectors
182+
- `createListenerMiddleware`: a side effects middleware for running logic in response to dispatched actions
183+
184+
Finally, the RTK package also includes "RTK Query", a full data fetching and caching solution for Redux apps, as a separate optional `@reduxjs/toolkit/query` entry point. It lets you define endpoints (REST, GraphQL, or any async function), and generates a reducer and middleware that fully manage fetching data, updating loading state, and caching results. It also automatically generates React hooks that can be used in components to fetch data, like `const { data, isFetching} = useGetPokemonQuery('pikachu')`
185+
186+
Each of these APIs is completely optional and designed for specific use cases, and **you can pick and choose which APIs you actually use in your app**. But, all of them are highly recommended to help with those tasks.
187+
188+
Note that **Redux Toolkit is still "Redux"!** There's still a single store, with dispatched action objects for updates, and reducers that immutably update state, plus the ability to write thunks for async logic, manage normalized state, type your code with TypeScript, and use the DevTools. **There's just way less code _you_ have to write for the same results!**
189+
190+
## Why We Want You To Use Redux Toolkit
191+
192+
As Redux maintainers, our opinion is:
193+
194+
:::tip
195+
196+
**We want _all_ Redux users to write their Redux code with Redux Toolkit, because it simplifies your code _and_ eliminates many common Redux mistakes and bugs!**
197+
198+
:::
199+
200+
The "boilerplate" and complexity of the early Redux patterns was never a _necessary_ part of Redux. Those patterns only existed because:
201+
202+
- The original "Flux Architecture" used some of those same approaches
203+
- The early Redux docs showed things like action type constants to enable separating code into different files by type
204+
- JavaScript is a mutable language by default, and writing immutable updates required manual object spreads and array updates
205+
- Redux was originally built in just a few weeks and intentionally designed to be just a few API primitives
206+
207+
Additionally, the Redux community has adopted some specific approaches that add additional boilerplate:
208+
209+
- Emphasizing use of the `redux-saga` middleware as a common approach for writing side effects
210+
- Insisting on hand-writing TS types for Redux action objects and creating union types to limit what actions can be dispatched at the type level
211+
212+
Over the years, we've seen how people actually used Redux in practice. We've seen how the community wrote hundreds of add-on libraries for tasks like generating action types and creators, async logic and side effects, and data fetching. We've also seen the problems that have consistently caused pain for our users, like accidentally mutating state, writing dozens of lines of code just to make one simple state update, and having trouble tracing how a codebase fits together. We've helped thousands of users who were trying to learn and use Redux and struggling to understand how all the pieces fit together, and were confused by the number of concepts and amount of extra code they had to write. We _know_ what problems our users are facing.
213+
214+
**We specifically designed Redux Toolkit to solve those problems!**
215+
216+
- Redux Toolkit simplifies store setup down to a single clear function call, while retaining the ability to fully configure the store's options if you need to
217+
- Redux Toolkit eliminates accidental mutations, which have always been the #1 cause of Redux bugs
218+
- Redux Toolkit eliminates the need to write any action creators or action types by hand
219+
- Redux Toolkit eliminates the need to write manual and error-prone immutable update logic
220+
- Redux Toolkit makes it easy to write a Redux feature's code in one file, instead of spreading it across multiple separate files
221+
- Redux Toolkit offers excellent TS support, with APIs that are designed to give you excellent type safety and minimize the number of types you have to define in your code
222+
- RTK Query can eliminate the need to write _any_ thunks, reducers, action creators, or effect hooks to manage fetching data and tracking loading state
223+
224+
Because of this:
225+
226+
:::tip
227+
228+
**We specifically recommend that our users _should_ use Redux Toolkit (the `@reduxjs/toolkit` package), and should _not_ use the legacy `redux` core package for any new Redux code today!**
229+
230+
:::
231+
232+
Even for existing applications, we recommend at least switching out `createStore` for `configureStore` as the dev-mode middleware will also help you catch accidental mutation and serializability errors in existing code bases. We also want to encourage you to switch the reducers you are using most (and any ones you write in the future) over to `createSlice` - the code will be shorter and easier to understand, and the safety improvements will save you time and effort going forward.
233+
234+
**The `redux` core package still works, but today we consider it to be obsolete**. All of its APIs are also re-exported from `@reduxjs/toolkit`, and `configureStore` does everything `createStore` does but with better default behavior and configurability.
235+
236+
It _is_ useful to understand the lower-level concepts, so that you have a better understanding of what Redux Toolkit is doing for you. That's why [the "Redux Fundamentals" tutorial shows how Redux works, with no abstractions](https://redux.js.org/tutorials/fundamentals/part-1-overview). _But_, it shows those examples solely as a learning tool, and finishes by showing you how Redux Toolkit simplifies the older hand-written Redux code.
237+
238+
If you are using the `redux` core package by itself, your code will continue to work. **But, we strongly encourage you to switch over to `@reduxjs/toolkit`, and update your code to use the Redux Toolkit APIs instead!**
239+
240+
## Further Information
241+
242+
See these docs pages and blog posts for more details
243+
244+
- [Redux Essentials: Redux Toolkit App Structure](https://redux.js.org/tutorials/essentials/part-2-app-structure)
245+
- [Redux Fundamentals: Modern Redux with Redux Toolkit](https://redux.js.org/tutorials/fundamentals/part-8-modern-redux)
246+
- [Redux Style Guide: Best Practices and Recommendations](https://redux.js.org/style-guide/)
247+
- [Presentation: Modern Redux with Redux Toolkit](https://blog.isquaredsoftware.com/2022/06/presentations-modern-redux-rtk/)
248+
- [Mark Erikson: Redux Toolkit 1.0 Announcement and development history](https://blog.isquaredsoftware.com/2019/10/redux-toolkit-1.0/)

docs/rtk-query/usage/persistence-and-rehydration.mdx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,28 +37,33 @@ box with the `autoMergeLevel1` or `autoMergeLevel2` [state reconcilers](https://
3737
when persisting the root reducer, or with the `autoMergeLevel1` reconciler when persisting just the api reducer.
3838

3939
```ts title="redux-persist rehydration example"
40-
import type { Action, PayloadAction } from '@reduxjs/toolkit'
40+
import type { Action } from '@reduxjs/toolkit'
4141
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
4242
import { REHYDRATE } from 'redux-persist'
4343

4444
type RootState = any // normally inferred from state
4545

46-
function isHydrateAction(action: Action): action is PayloadAction<RootState> {
46+
function isHydrateAction(action: Action): action is Action<typeof REHYDRATE> & {
47+
key: string
48+
payload: RootState
49+
err: unknown
50+
} {
4751
return action.type === REHYDRATE
4852
}
4953

5054
export const api = createApi({
5155
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
5256
// highlight-start
53-
extractRehydrationInfo(action, { reducerPath }) {
57+
// to prevent circular type issues, the return type needs to be annotated as any
58+
extractRehydrationInfo(action, { reducerPath }): any {
5459
if (isHydrateAction(action)) {
55-
if ((action as any).key === 'key used with redux-persist') {
56-
// when persisting the api reducer
60+
// when persisting the api reducer
61+
if (action.key === 'key used with redux-persist') {
5762
return action.payload
5863
}
5964

6065
// When persisting the root reducer
61-
return action.payload[reducerPath]
66+
return action.payload[api.reducerPath]
6267
}
6368
},
6469
// highlight-end

0 commit comments

Comments
 (0)