Skip to content

Commit 9c0d553

Browse files
committed
Merge branch 'master' into v2.0-integration
2 parents 7505f8e + e351a09 commit 9c0d553

33 files changed

+896
-241
lines changed

.github/FUNDING.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
github: [phryneas, markerikson]
1+
github: [phryneas, markerikson, EskiMojo14]

docs/api/configureStore.mdx

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,28 @@ hide_title: true
99

1010
# `configureStore`
1111

12-
A friendly abstraction over the standard Redux `createStore` function that adds good defaults
13-
to the store setup for a better development experience.
12+
The standard method for creating a Redux store. It uses the low-level Redux core `createStore` method internally, but wraps that to provide good defaults to the store setup for a better development experience.
13+
14+
## Purpose and Behavior
15+
16+
A standard Redux store setup typically requires multiple pieces of configuration:
17+
18+
- Combining the slice reducers into the root reducer
19+
- Creating the middleware enhancer, usually with the thunk middleware or other side effects middleware, as well as middleware that might be used for development checks
20+
- Adding the Redux DevTools enhancer, and composing the enhancers together
21+
- Calling `createStore`
22+
23+
Legacy Redux usage patterns typically required several dozen lines of copy-pasted boilerplate to achieve this.
24+
25+
Redux Toolkit's `configureStore` simplifies that setup process, by doing all that work for you. One call to `configureStore` will:
26+
27+
- Call `combineReducers` to combine your slices reducers into the root reducer function
28+
- Add the thunk middleware and called `applyMiddleware`
29+
- In development, automatically add more middleware to check for common mistakes like accidentally mutating the state
30+
- Automatically set up the Redux DevTools Extension connection
31+
- Call `createStore` to create a Redux store using that root reducer and those configuration options
32+
33+
`configureStore` also offers an improved API and usage patterns compared to the original `createStore` by accepting named fields for `reducer`, `preloadedState`, `middleware`, `enhancers`, and `devtools`, as well as much better TS type inference.
1434

1535
## Parameters
1636

docs/api/createSlice.mdx

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ and automatically generates action creators and action types that correspond to
1515
This API is the standard approach for writing Redux logic.
1616

1717
Internally, it uses [`createAction`](./createAction.mdx) and [`createReducer`](./createReducer.mdx), so
18-
you may also use [Immer](https://immerjs.github.io/immer/) to write "mutating" immutable updates:
18+
you may also use [Immer](../usage/immer-reducers.md) to write "mutating" immutable updates:
1919

2020
```ts
2121
import { createSlice } from '@reduxjs/toolkit'
@@ -321,16 +321,17 @@ reducers: (create) => {
321321

322322
### `extraReducers`
323323

324-
One of the key concepts of Redux is that each slice reducer "owns" its slice of state, and that many slice reducers
325-
can independently respond to the same action type. `extraReducers` allows `createSlice` to respond to other action types
326-
besides the types it has generated.
324+
Conceptually, each slice reducer "owns" its slice of state. There's also a natural correspondance between the update logic defined inside `reducers`, and the action types that are generated based on those.
327325

328-
As case reducers specified with `extraReducers` are meant to reference "external" actions, they will not have actions generated in `slice.actions`.
326+
However, there are many times that a Redux slice may also need to update its own state in response to action types that were defined elsewhere in the application (such as clearing many different kinds of data when a "user logged out" action is dispatched). This can include action types defined by another `createSlice` call, actions generated by a `createAsyncThunk`, RTK Query endpoint matchers, or any other action. In addition, one of the key concepts of Redux is that many slice reducers can independently respond to the same action type.
329327

330-
As with `reducers`, these case reducers will also be passed to `createReducer` and may "mutate" their state safely.
328+
**`extraReducers` allows `createSlice` to respond and update its own state in response to other action types besides the types it has generated.**
331329

332-
If two fields from `reducers` and `extraReducers` happen to end up with the same action type string,
333-
the function from `reducers` will be used to handle that action type.
330+
As with the `reducers` field, each case reducer in `extraReducers` is [wrapped in Immer and may use "mutating" syntax to safely update the state inside](../usage/immer-reducers.md).
331+
332+
However, unlike the `reducers` field, each individual case reducer inside of `extraReducers` will _not_ generate a new action type or action creator.
333+
334+
If two fields from `reducers` and `extraReducers` happen to end up with the same action type string, the function from `reducers` will be used to handle that action type.
334335

335336
#### The `extraReducers` "builder callback" notation
336337

@@ -372,6 +373,7 @@ const counterSlice = createSlice({
372373
})
373374
```
374375

376+
<<<<<<< HEAD
375377
This cycle can be fixed by providing an explicit return type for the selector:
376378

377379
```ts no-transpile
@@ -417,6 +419,10 @@ const counterSlice = createSlice({
417419

418420
:::
419421

422+
=======
423+
424+
> > > > > > > master
425+
420426
## Return Value
421427

422428
`createSlice` will return an object that looks like:

docs/api/matching-utilities.mdx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ As of Redux 5.0, action types are _required_ to be a string.
4242
A higher-order function that accepts one or more of:
4343

4444
- `redux-toolkit` action creator functions such as the ones produced by:
45-
- [`createAction`](./createAction)
46-
- [`createSlice`](./createSlice#return-value)
47-
- [`createAsyncThunk`](./createAsyncThunk#promise-lifecycle-actions)
45+
- [`createAction`](./createAction.mdx)
46+
- [`createSlice`](./createSlice.mdx#return-value)
47+
- [`createAsyncThunk`](./createAsyncThunk.mdx#promise-lifecycle-actions)
4848
- type guard functions
4949
- custom action creator functions that have a `.match` property that is a type guard
5050

@@ -56,7 +56,7 @@ Accepts the same inputs as `isAllOf` and will return a type guard function that
5656

5757
## `isAsyncThunkAction`
5858

59-
A higher-order function that returns a type guard function that may be used to check whether an action was created by [`createAsyncThunk`](./createAsyncThunk).
59+
A higher-order function that returns a type guard function that may be used to check whether an action was created by [`createAsyncThunk`](./createAsyncThunk.mdx).
6060

6161
```ts title="isAsyncThunkAction usage"
6262
import { isAsyncThunkAction } from '@reduxjs/toolkit'
@@ -128,7 +128,7 @@ function handleRejectedAction(action: UnknownAction) {
128128

129129
## `isRejectedWithValue`
130130

131-
A higher-order function that returns a type guard function that may be used to check whether an action is a 'rejected' action creator from the `createAsyncThunk` promise lifecycle that was created by [`rejectWithValue`](./createAsyncThunk#handling-thunk-errors).
131+
A higher-order function that returns a type guard function that may be used to check whether an action is a 'rejected' action creator from the `createAsyncThunk` promise lifecycle that was created by [`rejectWithValue`](./createAsyncThunk.mdx#handling-thunk-errors).
132132

133133
```ts title="isRejectedWithValue usage"
134134
import { isRejectedWithValue } from '@reduxjs/toolkit'

docs/rtk-query/api/createApi.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ See also [Invalidating cache data](../usage/automated-refetching.mdx#invalidatin
508508

509509
_(optional, only for query endpoints)_
510510

511-
Overrides the api-wide definition of `keepUnusedDataFor` for this endpoint only.a
511+
Overrides the api-wide definition of `keepUnusedDataFor` for this endpoint only.
512512

513513
[summary](docblock://query/createApi.ts?token=CreateApiOptions.keepUnusedDataFor)
514514

docs/rtk-query/api/created-api/api-slice-utils.mdx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ Some of the TS types on this page are pseudocode to illustrate intent, as the ac
2929
const updateQueryData = (
3030
endpointName: string,
3131
args: any,
32-
updateRecipe: (draft: Draft<CachedState>) => void
33-
) => ThunkAction<PatchCollection, PartialState, any, UnknownAction>;
32+
updateRecipe: (draft: Draft<CachedState>) => void,
33+
updateProvided?: boolean
34+
) => ThunkAction<PatchCollection, PartialState, any, AnyAction>;
3435

3536
interface PatchCollection {
3637
patches: Patch[];
@@ -43,6 +44,7 @@ interface PatchCollection {
4344
- `endpointName`: a string matching an existing endpoint name
4445
- `args`: an argument matching that used for a previous query call, used to determine which cached dataset needs to be updated
4546
- `updateRecipe`: an Immer `produce` callback that can apply changes to the cached state
47+
- `updateProvided`: a boolean indicating whether the endpoint's provided tags should be re-calculated based on the updated cache. Defaults to `false`.
4648

4749
#### Description
4850

@@ -155,14 +157,16 @@ await dispatch(
155157
const patchQueryData = (
156158
endpointName: string,
157159
args: any
158-
patches: Patch[]
160+
patches: Patch[],
161+
updateProvided?: boolean
159162
) => ThunkAction<void, PartialState, any, UnknownAction>;
160163
```
161164

162165
- **Parameters**
163166
- `endpointName`: a string matching an existing endpoint name
164167
- `args`: a cache key, used to determine which cached dataset needs to be updated
165168
- `patches`: an array of patches (or inverse patches) to apply to cached state. These would typically be obtained from the result of dispatching [`updateQueryData`](#updatequerydata)
169+
- `updateProvided`: a boolean indicating whether the endpoint's provided tags should be re-calculated based on the updated cache. Defaults to `false`.
166170

167171
#### Description
168172

@@ -255,11 +259,21 @@ function selectInvalidatedBy(
255259
A function that can select query parameters to be invalidated.
256260

257261
The function accepts two arguments
262+
<<<<<<< HEAD
263+
264+
=======
265+
266+
> > > > > > > master
258267

259268
- the root state and
260269
- the cache tags to be invalidated.
261270

262271
It returns an array that contains
272+
<<<<<<< HEAD
273+
274+
=======
275+
276+
> > > > > > > master
263277

264278
- the endpoint name,
265279
- the original args and

docs/rtk-query/overview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ import { createApi } from '@reduxjs/toolkit/query/react'
6767

6868
RTK Query includes these APIs:
6969

70-
- [`createApi()`](./api/createApi.mdx): The core of RTK Query's functionality. It allows you to define a set of endpoints describe how to retrieve data from a series of endpoints, including configuration of how to fetch and transform that data. In most cases, you should use this once per app, with "one API slice per base URL" as a rule of thumb.
70+
- [`createApi()`](./api/createApi.mdx): The core of RTK Query's functionality. It allows you to define a set of "endpoints" that describe how to retrieve data from backend APIs and other async sources, including the configuration of how to fetch and transform that data. In most cases, you should use this once per app, with "one API slice per base URL" as a rule of thumb.
7171
- [`fetchBaseQuery()`](./api/fetchBaseQuery.mdx): A small wrapper around [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) that aims to simplify requests. Intended as the recommended `baseQuery` to be used in `createApi` for the majority of users.
7272
- [`<ApiProvider />`](./api/ApiProvider.mdx): Can be used as a `Provider` if you **do not already have a Redux store**.
7373
- [`setupListeners()`](./api/setupListeners.mdx): A utility used to enable `refetchOnMount` and `refetchOnReconnect` behaviors.

docs/rtk-query/usage/customizing-queries.mdx

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,30 @@ See also [`baseQuery API Reference`](../api/createApi.mdx#basequery).
2626

2727
RTK Query expects a `baseQuery` function to be called with three arguments: `args`, `api`, and `extraOptions`. It is expected to return an object with either a `data` or `error` property, or a promise that resolves to return such an object.
2828

29+
:::tip
30+
31+
Base query and query functions must _always_ catch errors themselves, and return it in an object!
32+
33+
```ts no-transpile
34+
function brokenCustomBaseQuery() {
35+
// ❌ Don't let this throw by itself
36+
const data = await fetchSomeData()
37+
return { data }
38+
}
39+
40+
function correctCustomBaseQuery() {
41+
// ✅ Catch errors and _return_ them so the RTKQ logic can track it
42+
try {
43+
const data = await fetchSomeData()
44+
return { data }
45+
} catch (error) {
46+
return { error }
47+
}
48+
}
49+
```
50+
51+
:::
52+
2953
#### baseQuery function arguments
3054

3155
```ts title="baseQuery example arguments" no-transpile
@@ -232,13 +256,17 @@ transformErrorResponse: (response: Posts, meta, arg) => {
232256

233257
## Customizing queries with `queryFn`
234258

235-
Individual endpoints on [`createApi`](../api/createApi.mdx) accept a [`queryFn`](../api/createApi.mdx#queryfn) property which allows a given endpoint to ignore `baseQuery` for that endpoint by providing an inline function determining how that query resolves.
259+
RTK Query comes with `fetchBaseQuery` out of the box, which makes it straightforward to define endpoints that talk to HTTP URLs (such as a typical REST API). We also have integrations with GraphQL as well. However, at its core, RTK Query is really about tracking loading state and cached values for _any_ async request/response sequence, not just HTTP requests.
236260

237-
This can be useful for scenarios where you want to have particularly different behaviour for a single endpoint, or where the query itself is not relevant. Such situations may include:
261+
RTK Query supports defining endpoints that run arbitrary async logic and return a result. Individual endpoints on [`createApi`](../api/createApi.mdx) accept a [`queryFn`](../api/createApi.mdx#queryfn) property, which let you write your own async function with whatever logic you want inside.
262+
263+
This can be useful for scenarios where you want to have particularly different behaviour for a single endpoint, or where the query itself is not relevant, including:
238264

239265
- One-off queries that use a different base URL
240266
- One-off queries that use different request handling, such as automatic re-tries
241267
- One-off queries that use different error handling behaviour
268+
- Queries that make requests using a third-party library SDK, such as Firebase or Supabase
269+
- Queries that perform async tasks that are not a typical request/response
242270
- Performing multiple requests with a single query ([example](#performing-multiple-requests-with-a-single-query))
243271
- Leveraging invalidation behaviour with no relevant query ([example](#using-a-no-op-queryfn))
244272
- Using [Streaming Updates](./streaming-updates) with no relevant initial request ([example](#streaming-data-with-no-initial-request))
@@ -247,7 +275,39 @@ See also [`queryFn API Reference`](../api/createApi.mdx#queryfn) for the type si
247275

248276
### Implementing a `queryFn`
249277

250-
In order to use `queryFn`, it can be treated as an inline `baseQuery`. It will be called with the same arguments as `baseQuery`, as well as the provided `baseQuery` function itself (`arg`, `api`, `extraOptions`, and `baseQuery`). Similarly to `baseQuery`, it is expected to return an object with either a `data` or `error` property, or a promise that resolves to return such an object.
278+
A `queryFn` can be thought of as an inline `baseQuery`. It will be called with the same arguments as `baseQuery`, as well as the provided `baseQuery` function itself (`arg`, `api`, `extraOptions`, and `baseQuery`). Similarly to `baseQuery`, it is expected to return an object with either a `data` or `error` property, or a promise that resolves to return such an object.
279+
280+
#### Basic `queryFn` Example
281+
282+
```ts title="Basic queryFn example" no-transpile
283+
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
284+
import { userAPI, User } from './userAPI'
285+
286+
const api = createApi({
287+
baseQuery: fetchBaseQuery({ url: '/' }),
288+
endpoints: (build) => ({
289+
// normal HTTP endpoint using fetchBaseQuery
290+
getPosts: build.query<PostsResponse, void>({
291+
query: () => ({ url: 'posts' }),
292+
}),
293+
// highlight-start
294+
// endpoint with a custom `queryFn` and separate async logic
295+
getUser: build.query<User, string>({
296+
queryFn: async (userId: string) => {
297+
try {
298+
const user = await userApi.getUserById(userId)
299+
// Return the result in an object with a `data` field
300+
return { data: user }
301+
} catch (error) {
302+
// Catch any errors and return them as an object with an `error` field
303+
return { error }
304+
}
305+
},
306+
}),
307+
// highlight-end
308+
}),
309+
})
310+
```
251311

252312
#### queryFn function arguments
253313

@@ -878,6 +938,34 @@ export const { useGetPostsQuery } = api
878938
879939
## Examples - `queryFn`
880940
941+
### Using a Third-Party SDK
942+
943+
Many services like Firebase and Supabase provide their own SDK to make requests. You can use those SDK methods in a `queryFn`:
944+
945+
```ts title="Basic Third-Party SDK" no-transpile
946+
import { createApi, fakeBaseQuery } from '@reduxjs/toolkit/query/react'
947+
import { supabase } from './supabaseApi'
948+
949+
export const supabaseApi = createApi({
950+
reducerPath: 'supabaseApi',
951+
baseQuery: fakeBaseQuery(),
952+
endpoints: (builder) => ({
953+
getBlogs: builder.query({
954+
queryFn: async () => {
955+
// Supabase conveniently already has `data` and `error` fields
956+
const { data, error } = await supabase.from('blogs').select()
957+
if (error) {
958+
return { error }
959+
}
960+
return { data }
961+
},
962+
}),
963+
}),
964+
})
965+
```
966+
967+
You could also try creating a custom base query that uses the SDK, and define endpoints that pass method names or args into that base query.
968+
881969
### Using a no-op queryFn
882970

883971
In certain scenarios, you may wish to have a `query` or `mutation` where sending a request or returning data is not relevant for the situation. Such a scenario would be to leverage the `invalidatesTags` property to force re-fetch specific `tags` that have been provided to the cache.

0 commit comments

Comments
 (0)