Skip to content
This repository has been archived by the owner on Nov 23, 2024. It is now read-only.

Commit

Permalink
Merge branch 'next' into mutation-selectFromResult
Browse files Browse the repository at this point in the history
  • Loading branch information
msutkowski committed Apr 16, 2021
2 parents 4a8856f + f782b25 commit 09fe62a
Show file tree
Hide file tree
Showing 35 changed files with 653 additions and 307 deletions.
65 changes: 62 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
node: ['12.x', '14.x']
node: ['12.x']
os: [ubuntu-latest] #, windows-latest, macOS-latest

steps:
Expand All @@ -21,12 +21,71 @@ jobs:

- name: Install deps and build (with cache)
uses: bahmutov/npm-install@v1
with:
install-command: yarn --frozen-lockfile --ignore-scripts

- name: Lint
run: yarn lint

- name: Test
run: yarn test --ci --coverage --maxWorkers=2

- name: Build
run: yarn build
- name: Pack (including Prepare)
run: npm pack

- uses: actions/upload-artifact@v2
with:
name: package
path: rtk-incubator-rtk-query*.tgz

test:
name: Test Types with TypeScript ${{ matrix.ts }}

needs: [build]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node: ['12.x']
ts: ['3.9', '4.0', '4.1', '4.2', 'next']
steps:
- name: Checkout repo
uses: actions/checkout@v2

- name: Use node ${{ matrix.node }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node }}

- name: Install deps and build (with cache)
uses: bahmutov/npm-install@v1
with:
install-command: yarn --frozen-lockfile --ignore-scripts

- name: Install TypeScript ${{ matrix.ts }}
run: npm install typescript@${{ matrix.ts }} --ignore-scripts

- uses: actions/download-artifact@v2
with:
name: package

- name: Unpack build artifact to dist
run: tar -xzvf rtk-incubator-rtk-query-*.tgz --strip-components=1 package/dist

- name: Remap @redux/toolkit from src to dist
run: |
sed -i -re 's|(@rtk-incubator/rtk-query.*)\./src|\1./|' ./test/tsconfig.json
- name: '@ts-ignore @ts-expect-error messages in pre-3.9 in the tests'
if: ${{ matrix.ts < 3.9 }}
run: |
sed -i 's/@ts-expect-error/@ts-ignore/' test/*.ts*
- name: "@ts-ignore stuff that didn't exist pre-4.1 in the tests"
if: ${{ matrix.ts < 4.1 }}
run: sed -i -e 's/@pre41-ts-ignore/@ts-ignore/' -e '/pre41-remove-start/,/pre41-remove-end/d' test/*.ts*

- name: Test types
run: |
./node_modules/.bin/tsc --version
./node_modules/.bin/tsc --skipLibCheck -p test
14 changes: 13 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
{
"endOfLine": "auto"
"endOfLine": "auto",
"printWidth": 120,
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"overrides": [
{
"files": "*.ts",
"options": {
"parser": "babel-ts"
}
}
]
}
15 changes: 2 additions & 13 deletions docs/api/ApiProvider.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,9 @@ hide_title: true

# `ApiProvider`

Can be used as a `Provider` if you **do not already have a Redux store**.
[summary](docblock://react-hooks/ApiProvider.tsx?token=ApiProvider)

```ts title="Basic usage - wrap your App with ApiProvider"
import * as React from 'react';
import { ApiProvider } from '@rtk-incubator/rtk-query';

function App() {
return (
<ApiProvider api={api}>
<Pokemon />
</ApiProvider>
);
}
```
[examples](docblock://react-hooks/ApiProvider.tsx?token=ApiProvider)

:::danger
Using this together with an existing redux store will cause them to conflict with each other. If you are already using Redux, please use follow the instructions as shown in the [Getting Started guide](../introduction/getting-started).
Expand Down
43 changes: 22 additions & 21 deletions docs/api/createApi.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@ The main point where you will define a service to use in your application.

### `baseQuery`

[summary](docblock://createApi.ts?token=CreateApiOptions.baseQuery)

```ts title="Simulating axios-like interceptors with a custom base query"
const baseQuery = fetchBaseQuery({ baseUrl: '/' });

const baseQueryWithReauth: BaseQueryFn<
string | FetchArgs,
unknown,
FetchBaseQueryError
> = async (args, api, extraOptions) => {
const baseQueryWithReauth: BaseQueryFn<string | FetchArgs, unknown, FetchBaseQueryError> = async (
args,
api,
extraOptions
) => {
let result = await baseQuery(args, api, extraOptions);
if (result.error && result.error.status === '401') {
// try to get a new token
Expand All @@ -49,16 +51,16 @@ const baseQueryWithReauth: BaseQueryFn<
}
}
return result;
}
};
```

### `entityTypes`

Specifying entity types is optional, but you should define them so that they can be used for caching and invalidation. When defining an entity type, you will be able to add them with `provides` and [invalidate](../concepts/mutations#advanced-mutations-with-revalidation) them with `invalidates` when configuring [endpoints](#endpoints).
[summary](docblock://createApi.ts?token=CreateApiOptions.entityTypes)

### `reducerPath`

The `reducerPath` is a _unique_ key that your service will be mounted to in your store. If you call `createApi` more than once in your application, you will need to provide a unique value each time. Defaults to `api`.
[summary](docblock://createApi.ts?token=CreateApiOptions.reducerPath)

```js title="apis.js"
import { createApi, fetchBaseQuery } from '@rtk-incubator/rtk-query';
Expand All @@ -82,30 +84,31 @@ const apiTwo = createApi({

### `serializeQueryArgs`

Accepts a custom function if you have a need to change the creation of cache keys for any reason. Defaults to:
[summary](docblock://createApi.ts?token=CreateApiOptions.reducerPath)
Defaults to:

```ts no-compile
export const defaultSerializeQueryArgs: SerializeQueryArgs<any> = ({ endpoint, queryArgs }) => {
// Sort the object keys before stringifying, to prevent useQuery({ a: 1, b: 2 }) having a different cache key than useQuery({ b: 2, a: 1 })
// Sort the object keys before stringifying, to prevent useQuery({ a: 1, b: 2 }) having a different cache key than useQuery({ b: 2, a: 1 })
return `${endpoint}(${JSON.stringify(queryArgs, Object.keys(queryArgs || {}).sort())})`;
};
```

### `endpoints`

Endpoints are just a set of operations that you want to perform against your server. You define them as an object using the builder syntax. There are two basic endpoint types: [`query`](../concepts/queries) and [`mutation`](../concepts/mutations).
[summary](docblock://createApi.ts?token=CreateApiOptions.endpoints)

#### Anatomy of an endpoint

- `query` _(required)_
- `query` is the only required property, and can be either a `string` or an `object` that is passed to your `baseQuery`. If you are using [fetchBaseQuery](./fetchBaseQuery), this can be a `string` or an object of properties in `FetchArgs`. If you use your own custom `baseQuery`, you can customize this behavior to your liking
- [summary](docblock://endpointDefinitions.ts?token=EndpointDefinitionWithQuery.query)
- `transformResponse` _(optional)_

- A function to manipulate the data returned by a query or mutation
- [summary](docblock://endpointDefinitions.ts?token=EndpointDefinitionWithQuery.transformResponse)
- ```js title="Unpack a deeply nested collection"
transformResponse: (response) => response.some.nested.collection;
```
```js title="Normalize the response data"
- ```js title="Normalize the response data"
transformResponse: (response) =>
response.reduce((acc, curr) => {
acc[curr.id] = curr;
Expand All @@ -114,15 +117,12 @@ Endpoints are just a set of operations that you want to perform against your ser
```

- `provides` _(optional)_
- Used by `queries` to provide entities to the cache
- Expects an array of entity type strings, or an array of objects of entity types with ids.
1. `['Post']` - equivalent to 2
2. `[{ type: 'Post' }]` - equivalent to 1
3. `[{ type: 'Post', id: 1 }]`

[summary](docblock://endpointDefinitions.ts?token=QueryExtraOptions.provides)

- `invalidates` _(optional)_

- Used by `mutations` for [cache invalidation](../concepts/mutations#advanced-mutations-with-revalidation) purposes.
- Expects the same shapes as `provides`
[summary](docblock://endpointDefinitions.ts?token=MutationExtraOptions.invalidates)

- `onStart`, `onError` and `onSuccess` _(optional)_ - Available to both [queries](../concepts/queries) and [mutations](../concepts/mutations)
- Can be used in `mutations` for [optimistic updates](../concepts/optimistic-updates).
Expand Down Expand Up @@ -290,6 +290,7 @@ export const {
internalActions,
util,
injectEndpoints,
enhanceEndpoints,
usePrefetch,
...generatedHooks
} = api;
Expand Down
13 changes: 6 additions & 7 deletions docs/api/fetchBaseQuery.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,12 @@ export const pokemonApi = createApi({
query: (name: string) => `pokemon/${name}`, // Will make a request like https://pokeapi.co/api/v2/bulbasaur
}),
updatePokemon: builder.mutation({
query: ({ name, patch }) => ({
url: `pokemon/${name}`,
method: 'PATCH', // When performing a mutation, you typically use a method of PATCH/PUT/POST/DELETE for REST endpoints
body: patch, // fetchBaseQuery automatically adds `content-type: application/json` to the Headers and calls `JSON.stringify(patch)`
})
},
})
query: ({ name, patch }) => ({
url: `pokemon/${name}`,
method: 'PATCH', // When performing a mutation, you typically use a method of PATCH/PUT/POST/DELETE for REST endpoints
body: patch, // fetchBaseQuery automatically adds `content-type: application/json` to the Headers and calls `JSON.stringify(patch)`
}),
}),
}),
});
```
Expand Down
4 changes: 2 additions & 2 deletions docs/api/setupListeners.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ export function setupListeners(
}
```

If you notice, `onFocus`, `onFocusLost`, `onOffline`, `onOnline` are all actions that are provided to the callback. Additionally, these action are made available to `api.internalActions` and are able to be used by dispatching them like this:
If you notice, `onFocus`, `onFocusLost`, `onOffline`, `onOnline` are all actions that are provided to the callback. Additionally, these actions are made available to `api.internalActions` and are able to be used by dispatching them like this:

```ts title="Manual onFocus event"
dispatch(api.internalActions.onFocus())`
dispatch(api.internalActions.onFocus());
```
26 changes: 2 additions & 24 deletions docs/concepts/error-handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,31 +116,9 @@ RTK Query exports a utility called `retry` that you can wrap the `baseQuery` in
The default behavior would retry at these intervals:
1. 600ms + random time
2. 1200ms + random time
3. 2400ms + random time
4. 4800ms + random time
5. 9600ms + random time
```ts title="Retry every request 5 times by default"
// maxRetries: 5 is the default, and can be omitted. Shown for documentation purposes.
const staggeredBaseQuery = retry(fetchBaseQuery({ baseUrl: '/' }), { maxRetries: 5 });

export const api = createApi({
baseQuery: staggeredBaseQuery,
endpoints: (build) => ({
getPosts: build.query<PostsResponse, void>({
query: () => ({ url: 'posts' }),
}),
getPost: build.query<PostsResponse, void>({
query: (id: string) => ({ url: `posts/${id}` }),
extraOptions: { maxRetries: 8 }, // You can override the retry behavior on each endpoint
}),
}),
});
[remarks](docblock://retry.ts?token=defaultBackoff)
export const { useGetPostsQuery, useGetPostQuery } = api;
```
[examples](docblock://retry.ts?token=retry)
In the event that you didn't want to retry on a specific endpoint, you can just set `maxRetries: 0`.
Expand Down
24 changes: 17 additions & 7 deletions docs/concepts/mutations.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,36 @@ Notice the `onStart`, `onSuccess`, `onError` methods? Be sure to check out how t
### Type interfaces

```ts title="Mutation endpoint definition"
export interface MutationDefinition<
export type MutationDefinition<
QueryArg,
BaseQuery extends (arg: any, ...args: any[]) => any,
BaseQuery extends BaseQueryFn,
EntityTypes extends string,
ResultType,
ReducerPath extends string = string,
Context = Record<string, any>
> extends BaseEndpointDefinition<QueryArg, BaseQuery, ResultType> {
> = BaseEndpointDefinition<QueryArg, BaseQuery, ResultType> & {
type: DefinitionType.mutation;
invalidates?: ResultDescription<EntityTypes, ResultType, QueryArg>;
provides?: never;
onStart?(arg: QueryArg, mutationApi: MutationApi<ReducerPath, Context>): void;
onError?(arg: QueryArg, mutationApi: MutationApi<ReducerPath, Context>, error: unknown): void;
onSuccess?(arg: QueryArg, mutationApi: MutationApi<ReducerPath, Context>, result: ResultType): void;
}
onError?(
arg: QueryArg,
mutationApi: MutationApi<ReducerPath, Context>,
error: unknown,
meta: BaseQueryMeta<BaseQuery>
): void;
onSuccess?(
arg: QueryArg,
mutationApi: MutationApi<ReducerPath, Context>,
result: ResultType,
meta: BaseQueryMeta<BaseQuery> | undefined
): void;
};
```

```ts title="MutationApi"
export interface MutationApi<ReducerPath extends string, Context extends {}> {
dispatch: ThunkDispatch<RootState<any, any, ReducerPath>, unknown, AnyAction>;
dispatch: ThunkDispatch<any, any, AnyAction>;
getState(): RootState<any, any, ReducerPath>;
extra: unknown;
requestId: string;
Expand Down
9 changes: 2 additions & 7 deletions docs/concepts/prefetching.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,8 @@ usePrefetch<EndpointName extends QueryKeys<Definitions>>(

You can specify these prefetch options when declaring the hook or at the call site. The call site will take priority over the defaults.

1. `ifOlderThan` - (default: `false` | `number`) - _number is value in seconds_

- If specified, it will only run the query if the difference between `new Date()` and the last `fulfilledTimeStamp` is greater than the given value

2. `force`

- If `force: true`, it will ignore the `ifOlderThan` value if it is set and the query will be run even if it exists in the cache.
1. [summary](docblock://core/module.ts?token=PrefetchOptions)
2. [overloadSummary](docblock://core/module.ts?token=PrefetchOptions)

#### What to expect when you call the `callback`

Expand Down
2 changes: 1 addition & 1 deletion docs/concepts/queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ The way that this component is setup would have some nice traits:

### Selecting data from a query result

Sometimes you may have a parent component that is subscribed to a query, and then in a child component you want to pick an item from that query. In most cases you don't want to perform an additional request for a `getItemById`-type query when you know that you already have the result. `selectFromResult` allows you to get a specific segment from a query result in a performant manner. When using this feature, the component will not rerender unless the underlying data of the selected item has changed. If the selected item is one elemnt in a larger collection, it will disregard changes to elements in the same collection.
Sometimes you may have a parent component that is subscribed to a query, and then in a child component you want to pick an item from that query. In most cases you don't want to perform an additional request for a `getItemById`-type query when you know that you already have the result. `selectFromResult` allows you to get a specific segment from a query result in a performant manner. When using this feature, the component will not rerender unless the underlying data of the selected item has changed. If the selected item is one element in a larger collection, it will disregard changes to elements in the same collection.

```ts title="Using selectFromResult to extract a single result"
function PostsList() {
Expand Down
Loading

0 comments on commit 09fe62a

Please sign in to comment.