From 3ca76403a2ea0f68d867e488609130cf5528b393 Mon Sep 17 00:00:00 2001 From: Arya Emami Date: Sun, 18 Feb 2024 01:46:45 -0600 Subject: [PATCH] Converted all CRLF files to LF --- examples/query/react/advanced/src/Pokemon.tsx | 66 +- .../react/advanced/src/services/pokemon.ts | 30 +- .../src/mocks/browser.ts | 8 +- .../react/authentication/src/mocks/browser.ts | 8 +- .../query/react/basic/src/services/pokemon.ts | 28 +- examples/query/react/basic/src/store.ts | 42 +- .../src/services/pokemon.ts | 30 +- .../react/conditional-fetching/src/store.ts | 22 +- .../deduping-queries/src/services/pokemon.ts | 26 +- .../query/react/deduping-queries/src/store.ts | 22 +- .../src/app/services/baseApi.ts | 18 +- .../src/features/posts/PostsManager.tsx | 224 +++--- .../graphql-codegen/src/mocks/browser.ts | 8 +- .../react/graphql/src/app/services/posts.ts | 154 ++-- .../src/features/posts/PostsManager.tsx | 220 +++--- .../query/react/graphql/src/mocks/browser.ts | 8 +- .../kitchen-sink/src/app/services/counter.ts | 80 +- .../query/react/kitchen-sink/src/app/store.ts | 52 +- .../src/features/counter/Counter.module.css | 154 ++-- .../src/features/counter/Counter.tsx | 104 +-- .../src/features/posts/PostDetail.tsx | 262 +++---- .../src/features/posts/PostsManager.css | 20 +- .../src/features/posts/PostsManager.tsx | 282 +++---- .../react/kitchen-sink/src/mocks/browser.ts | 8 +- .../react/mutations/src/app/services/posts.ts | 124 +-- .../src/features/posts/PostDetail.tsx | 306 ++++---- .../src/features/posts/PostsManager.tsx | 328 ++++---- .../react/mutations/src/mocks/browser.ts | 8 +- examples/query/react/mutations/src/store.ts | 22 +- .../src/app/services/posts.ts | 148 ++-- .../src/features/posts/PostDetail.tsx | 306 ++++---- .../src/features/posts/PostsManager.tsx | 384 +++++----- .../optimistic-update/src/mocks/browser.ts | 8 +- .../pagination/src/app/services/posts.ts | 62 +- .../src/features/posts/PostsManager.tsx | 220 +++--- .../react/pagination/src/mocks/browser.ts | 8 +- .../react/polling/src/services/pokemon.ts | 30 +- examples/query/react/polling/src/store.ts | 22 +- .../src/app/services/posts.ts | 62 +- .../src/features/posts/PostsManager.tsx | 260 +++---- .../src/mocks/browser.ts | 8 +- .../src/app/services/posts.ts | 62 +- .../src/features/posts/PostsManager.tsx | 244 +++--- .../src/mocks/browser.ts | 8 +- .../prefetching/src/app/services/posts.ts | 62 +- .../src/features/posts/PostsManager.tsx | 232 +++--- .../react/prefetching/src/mocks/browser.ts | 8 +- packages/toolkit/api-extractor-react.json | 712 +++++++++--------- packages/toolkit/api-extractor.json | 712 +++++++++--------- packages/toolkit/src/entities/models.ts | 396 +++++----- .../src/entities/sorted_state_adapter.ts | 336 ++++----- .../tests/entity_slice_enhancer.test.ts | 118 +-- website/README.md | 66 +- website/sidebars.json | 300 ++++---- website/src/js/monokaiTheme.js | 124 +-- website/src/pages/index.js | 426 +++++------ website/src/pages/styles.module.css | 220 +++--- 57 files changed, 4104 insertions(+), 4104 deletions(-) diff --git a/examples/query/react/advanced/src/Pokemon.tsx b/examples/query/react/advanced/src/Pokemon.tsx index 4e848e96..9746ec0d 100644 --- a/examples/query/react/advanced/src/Pokemon.tsx +++ b/examples/query/react/advanced/src/Pokemon.tsx @@ -1,33 +1,33 @@ -import { useGetPokemonByNameQuery } from './services/pokemon' - -export const Pokemon = ({ - name, - pollingInterval, -}: { - name: string - pollingInterval: number -}) => { - const { data, error, isLoading, isFetching } = useGetPokemonByNameQuery( - name, - { - pollingInterval, - }, - ) - - return ( - <> - {error ? ( - <>Oh no, there was an error - ) : isLoading ? ( - <>Loading... - ) : data ? ( - <> -

- {data.species.name} {isFetching ? '...' : ''} -

- {data.species.name} - - ) : null} - - ) -} +import { useGetPokemonByNameQuery } from './services/pokemon' + +export const Pokemon = ({ + name, + pollingInterval, +}: { + name: string + pollingInterval: number +}) => { + const { data, error, isLoading, isFetching } = useGetPokemonByNameQuery( + name, + { + pollingInterval, + }, + ) + + return ( + <> + {error ? ( + <>Oh no, there was an error + ) : isLoading ? ( + <>Loading... + ) : data ? ( + <> +

+ {data.species.name} {isFetching ? '...' : ''} +

+ {data.species.name} + + ) : null} + + ) +} diff --git a/examples/query/react/advanced/src/services/pokemon.ts b/examples/query/react/advanced/src/services/pokemon.ts index d9f7890a..5ec3234f 100644 --- a/examples/query/react/advanced/src/services/pokemon.ts +++ b/examples/query/react/advanced/src/services/pokemon.ts @@ -1,15 +1,15 @@ -import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' - -export const pokemonApi = createApi({ - reducerPath: 'pokemonApi', - baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }), - tagTypes: [], - endpoints: (builder) => ({ - getPokemonByName: builder.query({ - query: (name: string) => `pokemon/${name}`, - }), - }), -}) - -// Export hooks for usage in functional components -export const { useGetPokemonByNameQuery } = pokemonApi +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' + +export const pokemonApi = createApi({ + reducerPath: 'pokemonApi', + baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }), + tagTypes: [], + endpoints: (builder) => ({ + getPokemonByName: builder.query({ + query: (name: string) => `pokemon/${name}`, + }), + }), +}) + +// Export hooks for usage in functional components +export const { useGetPokemonByNameQuery } = pokemonApi diff --git a/examples/query/react/authentication-with-extrareducers/src/mocks/browser.ts b/examples/query/react/authentication-with-extrareducers/src/mocks/browser.ts index d8d2c7d7..b234fdae 100644 --- a/examples/query/react/authentication-with-extrareducers/src/mocks/browser.ts +++ b/examples/query/react/authentication-with-extrareducers/src/mocks/browser.ts @@ -1,4 +1,4 @@ -import { setupWorker } from 'msw' -import { handlers } from './handlers' - -export const worker = setupWorker(...handlers) +import { setupWorker } from 'msw' +import { handlers } from './handlers' + +export const worker = setupWorker(...handlers) diff --git a/examples/query/react/authentication/src/mocks/browser.ts b/examples/query/react/authentication/src/mocks/browser.ts index d8d2c7d7..b234fdae 100644 --- a/examples/query/react/authentication/src/mocks/browser.ts +++ b/examples/query/react/authentication/src/mocks/browser.ts @@ -1,4 +1,4 @@ -import { setupWorker } from 'msw' -import { handlers } from './handlers' - -export const worker = setupWorker(...handlers) +import { setupWorker } from 'msw' +import { handlers } from './handlers' + +export const worker = setupWorker(...handlers) diff --git a/examples/query/react/basic/src/services/pokemon.ts b/examples/query/react/basic/src/services/pokemon.ts index 1d2e0316..0a2b88e6 100644 --- a/examples/query/react/basic/src/services/pokemon.ts +++ b/examples/query/react/basic/src/services/pokemon.ts @@ -1,14 +1,14 @@ -import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' - -export const pokemonApi = createApi({ - baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }), - tagTypes: [], - endpoints: (builder) => ({ - getPokemonByName: builder.query({ - query: (name: string) => `pokemon/${name}`, - }), - }), -}) - -// Export hooks for usage in functional components -export const { useGetPokemonByNameQuery } = pokemonApi +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' + +export const pokemonApi = createApi({ + baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }), + tagTypes: [], + endpoints: (builder) => ({ + getPokemonByName: builder.query({ + query: (name: string) => `pokemon/${name}`, + }), + }), +}) + +// Export hooks for usage in functional components +export const { useGetPokemonByNameQuery } = pokemonApi diff --git a/examples/query/react/basic/src/store.ts b/examples/query/react/basic/src/store.ts index 22c59dd8..58e58a55 100644 --- a/examples/query/react/basic/src/store.ts +++ b/examples/query/react/basic/src/store.ts @@ -1,21 +1,21 @@ -import { combineReducers, configureStore } from '@reduxjs/toolkit' -import type { PreloadedState } from '@reduxjs/toolkit' -import { pokemonApi } from './services/pokemon' - -const rootReducer = combineReducers({ - [pokemonApi.reducerPath]: pokemonApi.reducer, -}) - -export const setupStore = (preloadedState?: PreloadedState) => { - return configureStore({ - reducer: rootReducer, - middleware: (getDefaultMiddleware) => - // adding the api middleware enables caching, invalidation, polling and other features of `rtk-query` - getDefaultMiddleware().concat(pokemonApi.middleware), - preloadedState, - }) -} - -export type RootState = ReturnType -export type AppStore = ReturnType -export type AppDispatch = AppStore['dispatch'] +import { combineReducers, configureStore } from '@reduxjs/toolkit' +import type { PreloadedState } from '@reduxjs/toolkit' +import { pokemonApi } from './services/pokemon' + +const rootReducer = combineReducers({ + [pokemonApi.reducerPath]: pokemonApi.reducer, +}) + +export const setupStore = (preloadedState?: PreloadedState) => { + return configureStore({ + reducer: rootReducer, + middleware: (getDefaultMiddleware) => + // adding the api middleware enables caching, invalidation, polling and other features of `rtk-query` + getDefaultMiddleware().concat(pokemonApi.middleware), + preloadedState, + }) +} + +export type RootState = ReturnType +export type AppStore = ReturnType +export type AppDispatch = AppStore['dispatch'] diff --git a/examples/query/react/conditional-fetching/src/services/pokemon.ts b/examples/query/react/conditional-fetching/src/services/pokemon.ts index b338a20b..fb7cc5c1 100644 --- a/examples/query/react/conditional-fetching/src/services/pokemon.ts +++ b/examples/query/react/conditional-fetching/src/services/pokemon.ts @@ -1,15 +1,15 @@ -import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' -import type { PokemonName } from '../pokemon.data' - -export const pokemonApi = createApi({ - reducerPath: 'pokemonApi', - baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }), - endpoints: (builder) => ({ - getPokemonByName: builder.query({ - query: (name: PokemonName) => `pokemon/${name}`, - }), - }), -}) - -// Export hooks for usage in functional components -export const { useGetPokemonByNameQuery } = pokemonApi +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' +import type { PokemonName } from '../pokemon.data' + +export const pokemonApi = createApi({ + reducerPath: 'pokemonApi', + baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }), + endpoints: (builder) => ({ + getPokemonByName: builder.query({ + query: (name: PokemonName) => `pokemon/${name}`, + }), + }), +}) + +// Export hooks for usage in functional components +export const { useGetPokemonByNameQuery } = pokemonApi diff --git a/examples/query/react/conditional-fetching/src/store.ts b/examples/query/react/conditional-fetching/src/store.ts index 8168bb91..69b71824 100644 --- a/examples/query/react/conditional-fetching/src/store.ts +++ b/examples/query/react/conditional-fetching/src/store.ts @@ -1,11 +1,11 @@ -import { configureStore } from '@reduxjs/toolkit' -import { pokemonApi } from './services/pokemon' - -export const store = configureStore({ - reducer: { - [pokemonApi.reducerPath]: pokemonApi.reducer, - }, - // adding the api middleware enables caching, invalidation, polling and other features of `rtk-query` - middleware: (getDefaultMiddleware) => - getDefaultMiddleware().concat(pokemonApi.middleware), -}) +import { configureStore } from '@reduxjs/toolkit' +import { pokemonApi } from './services/pokemon' + +export const store = configureStore({ + reducer: { + [pokemonApi.reducerPath]: pokemonApi.reducer, + }, + // adding the api middleware enables caching, invalidation, polling and other features of `rtk-query` + middleware: (getDefaultMiddleware) => + getDefaultMiddleware().concat(pokemonApi.middleware), +}) diff --git a/examples/query/react/deduping-queries/src/services/pokemon.ts b/examples/query/react/deduping-queries/src/services/pokemon.ts index 2dfb0463..67f55e3b 100644 --- a/examples/query/react/deduping-queries/src/services/pokemon.ts +++ b/examples/query/react/deduping-queries/src/services/pokemon.ts @@ -1,13 +1,13 @@ -import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' - -export const pokemonApi = createApi({ - baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }), - endpoints: (builder) => ({ - getPokemonByName: builder.query({ - query: (name: string) => `pokemon/${name}`, - }), - }), -}) - -// Export hooks for usage in functional components -export const { useGetPokemonByNameQuery } = pokemonApi +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' + +export const pokemonApi = createApi({ + baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }), + endpoints: (builder) => ({ + getPokemonByName: builder.query({ + query: (name: string) => `pokemon/${name}`, + }), + }), +}) + +// Export hooks for usage in functional components +export const { useGetPokemonByNameQuery } = pokemonApi diff --git a/examples/query/react/deduping-queries/src/store.ts b/examples/query/react/deduping-queries/src/store.ts index 8168bb91..69b71824 100644 --- a/examples/query/react/deduping-queries/src/store.ts +++ b/examples/query/react/deduping-queries/src/store.ts @@ -1,11 +1,11 @@ -import { configureStore } from '@reduxjs/toolkit' -import { pokemonApi } from './services/pokemon' - -export const store = configureStore({ - reducer: { - [pokemonApi.reducerPath]: pokemonApi.reducer, - }, - // adding the api middleware enables caching, invalidation, polling and other features of `rtk-query` - middleware: (getDefaultMiddleware) => - getDefaultMiddleware().concat(pokemonApi.middleware), -}) +import { configureStore } from '@reduxjs/toolkit' +import { pokemonApi } from './services/pokemon' + +export const store = configureStore({ + reducer: { + [pokemonApi.reducerPath]: pokemonApi.reducer, + }, + // adding the api middleware enables caching, invalidation, polling and other features of `rtk-query` + middleware: (getDefaultMiddleware) => + getDefaultMiddleware().concat(pokemonApi.middleware), +}) diff --git a/examples/query/react/graphql-codegen/src/app/services/baseApi.ts b/examples/query/react/graphql-codegen/src/app/services/baseApi.ts index 8e5f6919..aa2cf22d 100644 --- a/examples/query/react/graphql-codegen/src/app/services/baseApi.ts +++ b/examples/query/react/graphql-codegen/src/app/services/baseApi.ts @@ -1,9 +1,9 @@ -import { createApi } from '@reduxjs/toolkit/query/react' -import { graphqlRequestBaseQuery } from '@rtk-query/graphql-request-base-query' - -export const api = createApi({ - baseQuery: graphqlRequestBaseQuery({ - url: '/graphql', - }), - endpoints: () => ({}), -}) +import { createApi } from '@reduxjs/toolkit/query/react' +import { graphqlRequestBaseQuery } from '@rtk-query/graphql-request-base-query' + +export const api = createApi({ + baseQuery: graphqlRequestBaseQuery({ + url: '/graphql', + }), + endpoints: () => ({}), +}) diff --git a/examples/query/react/graphql-codegen/src/features/posts/PostsManager.tsx b/examples/query/react/graphql-codegen/src/features/posts/PostsManager.tsx index 9eefa7bf..82052c13 100644 --- a/examples/query/react/graphql-codegen/src/features/posts/PostsManager.tsx +++ b/examples/query/react/graphql-codegen/src/features/posts/PostsManager.tsx @@ -1,112 +1,112 @@ -import * as React from 'react' -import { - Badge, - Box, - Button, - Divider, - Flex, - Heading, - HStack, - Icon, - List, - ListIcon, - ListItem, - Spacer, - Stat, - StatLabel, - StatNumber, -} from '@chakra-ui/react' -import { MdArrowBack, MdArrowForward, MdBook } from 'react-icons/md' -import { useGetPostsQuery } from './GetPosts.generated' - -const getColorForStatus = (status: string | undefined) => { - return status === 'draft' - ? 'gray' - : status === 'pending_review' - ? 'orange' - : 'green' -} - -const PostList = () => { - const [page, setPage] = React.useState(1) - const { data, isLoading, isFetching } = useGetPostsQuery({ - skip: page * 10, - take: 10, - }) - - if (isLoading) { - return
Loading
- } - - return ( - - - - - {`${page}`} - - - {data?.posts?.length ? ( - data?.posts.map(({ id, title, status }) => ( - - {title}{' '} - - {status} - - - )) - ) : ( - No posts :( - )} - - - ) -} - -export const PostsCountStat = () => { - const { data } = useGetPostsQuery({}) - - return ( - - Total Posts - {`${data?.posts?.length || 'NA'}`} - - ) -} - -export const PostsManager = () => { - return ( - - - - Manage Posts - - - - - - - - - - - - ) -} - -export default PostsManager +import * as React from 'react' +import { + Badge, + Box, + Button, + Divider, + Flex, + Heading, + HStack, + Icon, + List, + ListIcon, + ListItem, + Spacer, + Stat, + StatLabel, + StatNumber, +} from '@chakra-ui/react' +import { MdArrowBack, MdArrowForward, MdBook } from 'react-icons/md' +import { useGetPostsQuery } from './GetPosts.generated' + +const getColorForStatus = (status: string | undefined) => { + return status === 'draft' + ? 'gray' + : status === 'pending_review' + ? 'orange' + : 'green' +} + +const PostList = () => { + const [page, setPage] = React.useState(1) + const { data, isLoading, isFetching } = useGetPostsQuery({ + skip: page * 10, + take: 10, + }) + + if (isLoading) { + return
Loading
+ } + + return ( + + + + + {`${page}`} + + + {data?.posts?.length ? ( + data?.posts.map(({ id, title, status }) => ( + + {title}{' '} + + {status} + + + )) + ) : ( + No posts :( + )} + + + ) +} + +export const PostsCountStat = () => { + const { data } = useGetPostsQuery({}) + + return ( + + Total Posts + {`${data?.posts?.length || 'NA'}`} + + ) +} + +export const PostsManager = () => { + return ( + + + + Manage Posts + + + + + + + + + + + + ) +} + +export default PostsManager diff --git a/examples/query/react/graphql-codegen/src/mocks/browser.ts b/examples/query/react/graphql-codegen/src/mocks/browser.ts index b741446c..82d072ec 100644 --- a/examples/query/react/graphql-codegen/src/mocks/browser.ts +++ b/examples/query/react/graphql-codegen/src/mocks/browser.ts @@ -1,4 +1,4 @@ -import { setupWorker } from 'msw' -import { handlers } from './db' - -export const worker = setupWorker(...handlers) +import { setupWorker } from 'msw' +import { handlers } from './db' + +export const worker = setupWorker(...handlers) diff --git a/examples/query/react/graphql/src/app/services/posts.ts b/examples/query/react/graphql/src/app/services/posts.ts index 0d84c75f..ff5230e7 100644 --- a/examples/query/react/graphql/src/app/services/posts.ts +++ b/examples/query/react/graphql/src/app/services/posts.ts @@ -1,77 +1,77 @@ -import { createApi } from '@reduxjs/toolkit/query/react' -import { gql } from 'graphql-request' -import { graphqlRequestBaseQuery } from '@rtk-query/graphql-request-base-query' - -export const postStatuses = ['draft', 'published', 'pending_review'] as const - -export interface Post { - id: string - title: string - author: string - content: string - status: (typeof postStatuses)[number] - created_at: string - updated_at: string -} - -export interface Pagination { - page: number - per_page: number - total: number - total_pages: number -} - -export interface GetPostsResponse extends Pagination { - data: { - posts: Post[] - } -} - -interface PostResponse { - data: { - post: Post - } -} - -export const api = createApi({ - baseQuery: graphqlRequestBaseQuery({ - url: '/graphql', - }), - endpoints: (builder) => ({ - getPosts: builder.query< - GetPostsResponse, - { page?: number; per_page?: number } - >({ - query: ({ page, per_page }) => ({ - document: gql` - query GetPosts($page: Int = 1, $per_page: Int = 10) { - posts(page: $page, per_page: $per_page) { - id - title - } - } - `, - variables: { - page, - per_page, - }, - }), - }), - getPost: builder.query({ - query: (id) => ({ - document: gql` - query GetPost($id: ID!) { - post(id: ${id}) { - id - title - body - } - } - `, - }), - transformResponse: (response: PostResponse) => response.data.post, - }), - }), -}) - -export const { useGetPostsQuery, useGetPostQuery } = api +import { createApi } from '@reduxjs/toolkit/query/react' +import { gql } from 'graphql-request' +import { graphqlRequestBaseQuery } from '@rtk-query/graphql-request-base-query' + +export const postStatuses = ['draft', 'published', 'pending_review'] as const + +export interface Post { + id: string + title: string + author: string + content: string + status: (typeof postStatuses)[number] + created_at: string + updated_at: string +} + +export interface Pagination { + page: number + per_page: number + total: number + total_pages: number +} + +export interface GetPostsResponse extends Pagination { + data: { + posts: Post[] + } +} + +interface PostResponse { + data: { + post: Post + } +} + +export const api = createApi({ + baseQuery: graphqlRequestBaseQuery({ + url: '/graphql', + }), + endpoints: (builder) => ({ + getPosts: builder.query< + GetPostsResponse, + { page?: number; per_page?: number } + >({ + query: ({ page, per_page }) => ({ + document: gql` + query GetPosts($page: Int = 1, $per_page: Int = 10) { + posts(page: $page, per_page: $per_page) { + id + title + } + } + `, + variables: { + page, + per_page, + }, + }), + }), + getPost: builder.query({ + query: (id) => ({ + document: gql` + query GetPost($id: ID!) { + post(id: ${id}) { + id + title + body + } + } + `, + }), + transformResponse: (response: PostResponse) => response.data.post, + }), + }), +}) + +export const { useGetPostsQuery, useGetPostQuery } = api diff --git a/examples/query/react/graphql/src/features/posts/PostsManager.tsx b/examples/query/react/graphql/src/features/posts/PostsManager.tsx index 8203ed4b..da92a4ee 100644 --- a/examples/query/react/graphql/src/features/posts/PostsManager.tsx +++ b/examples/query/react/graphql/src/features/posts/PostsManager.tsx @@ -1,110 +1,110 @@ -import * as React from 'react' -import { - Badge, - Box, - Button, - Divider, - Flex, - Heading, - HStack, - Icon, - List, - ListIcon, - ListItem, - Spacer, - Stat, - StatLabel, - StatNumber, -} from '@chakra-ui/react' -import { MdArrowBack, MdArrowForward, MdBook } from 'react-icons/md' -import { Post, useGetPostsQuery } from '../../app/services/posts' - -const getColorForStatus = (status: Post['status']) => { - return status === 'draft' - ? 'gray' - : status === 'pending_review' - ? 'orange' - : 'green' -} - -const PostList = () => { - const [page, setPage] = React.useState(1) - const { data: posts, isLoading, isFetching } = useGetPostsQuery({ page }) - - if (isLoading) { - return
Loading
- } - - if (!posts?.data) { - return
No posts :(
- } - - return ( - - - - - {`${page} / ${posts.total_pages}`} - - - {posts?.data.posts.map(({ id, title, status }) => ( - - {title}{' '} - - {status} - - - ))} - - - ) -} - -export const PostsCountStat = () => { - const { data: posts } = useGetPostsQuery({}) - - return ( - - Total Posts - {`${posts?.total || 'NA'}`} - - ) -} - -export const PostsManager = () => { - return ( - - - - Manage Posts - - - - - - - - - - - - ) -} - -export default PostsManager +import * as React from 'react' +import { + Badge, + Box, + Button, + Divider, + Flex, + Heading, + HStack, + Icon, + List, + ListIcon, + ListItem, + Spacer, + Stat, + StatLabel, + StatNumber, +} from '@chakra-ui/react' +import { MdArrowBack, MdArrowForward, MdBook } from 'react-icons/md' +import { Post, useGetPostsQuery } from '../../app/services/posts' + +const getColorForStatus = (status: Post['status']) => { + return status === 'draft' + ? 'gray' + : status === 'pending_review' + ? 'orange' + : 'green' +} + +const PostList = () => { + const [page, setPage] = React.useState(1) + const { data: posts, isLoading, isFetching } = useGetPostsQuery({ page }) + + if (isLoading) { + return
Loading
+ } + + if (!posts?.data) { + return
No posts :(
+ } + + return ( + + + + + {`${page} / ${posts.total_pages}`} + + + {posts?.data.posts.map(({ id, title, status }) => ( + + {title}{' '} + + {status} + + + ))} + + + ) +} + +export const PostsCountStat = () => { + const { data: posts } = useGetPostsQuery({}) + + return ( + + Total Posts + {`${posts?.total || 'NA'}`} + + ) +} + +export const PostsManager = () => { + return ( + + + + Manage Posts + + + + + + + + + + + + ) +} + +export default PostsManager diff --git a/examples/query/react/graphql/src/mocks/browser.ts b/examples/query/react/graphql/src/mocks/browser.ts index b741446c..82d072ec 100644 --- a/examples/query/react/graphql/src/mocks/browser.ts +++ b/examples/query/react/graphql/src/mocks/browser.ts @@ -1,4 +1,4 @@ -import { setupWorker } from 'msw' -import { handlers } from './db' - -export const worker = setupWorker(...handlers) +import { setupWorker } from 'msw' +import { handlers } from './db' + +export const worker = setupWorker(...handlers) diff --git a/examples/query/react/kitchen-sink/src/app/services/counter.ts b/examples/query/react/kitchen-sink/src/app/services/counter.ts index de350e18..0386a412 100644 --- a/examples/query/react/kitchen-sink/src/app/services/counter.ts +++ b/examples/query/react/kitchen-sink/src/app/services/counter.ts @@ -1,40 +1,40 @@ -import { api } from './api' - -interface CountResponse { - count: number -} - -export const counterApi = api.injectEndpoints({ - endpoints: (build) => ({ - getCount: build.query({ - query: () => 'count', - providesTags: ['Counter'], - }), - incrementCount: build.mutation({ - query(amount) { - return { - url: `increment`, - method: 'PUT', - body: { amount }, - } - }, - invalidatesTags: ['Counter'], - }), - decrementCount: build.mutation({ - query(amount) { - return { - url: `decrement`, - method: 'PUT', - body: { amount }, - } - }, - invalidatesTags: ['Counter'], - }), - }), -}) - -export const { - useDecrementCountMutation, - useGetCountQuery, - useIncrementCountMutation, -} = counterApi +import { api } from './api' + +interface CountResponse { + count: number +} + +export const counterApi = api.injectEndpoints({ + endpoints: (build) => ({ + getCount: build.query({ + query: () => 'count', + providesTags: ['Counter'], + }), + incrementCount: build.mutation({ + query(amount) { + return { + url: `increment`, + method: 'PUT', + body: { amount }, + } + }, + invalidatesTags: ['Counter'], + }), + decrementCount: build.mutation({ + query(amount) { + return { + url: `decrement`, + method: 'PUT', + body: { amount }, + } + }, + invalidatesTags: ['Counter'], + }), + }), +}) + +export const { + useDecrementCountMutation, + useGetCountQuery, + useIncrementCountMutation, +} = counterApi diff --git a/examples/query/react/kitchen-sink/src/app/store.ts b/examples/query/react/kitchen-sink/src/app/store.ts index e814615c..fe8c2b61 100644 --- a/examples/query/react/kitchen-sink/src/app/store.ts +++ b/examples/query/react/kitchen-sink/src/app/store.ts @@ -1,26 +1,26 @@ -import { configureStore, ConfigureStoreOptions } from '@reduxjs/toolkit' -import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux' -import { api } from './services/api' -import polling from '../features/polling/pollingSlice' -import auth from '../features/auth/authSlice' - -export const createStore = ( - options?: ConfigureStoreOptions['preloadedState'] | undefined, -) => - configureStore({ - reducer: { - [api.reducerPath]: api.reducer, - polling, - auth, - }, - middleware: (getDefaultMiddleware) => - getDefaultMiddleware().concat(api.middleware), - ...options, - }) - -export const store = createStore() - -export type AppDispatch = typeof store.dispatch -export const useAppDispatch: () => AppDispatch = useDispatch -export type RootState = ReturnType -export const useTypedSelector: TypedUseSelectorHook = useSelector +import { configureStore, ConfigureStoreOptions } from '@reduxjs/toolkit' +import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux' +import { api } from './services/api' +import polling from '../features/polling/pollingSlice' +import auth from '../features/auth/authSlice' + +export const createStore = ( + options?: ConfigureStoreOptions['preloadedState'] | undefined, +) => + configureStore({ + reducer: { + [api.reducerPath]: api.reducer, + polling, + auth, + }, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware().concat(api.middleware), + ...options, + }) + +export const store = createStore() + +export type AppDispatch = typeof store.dispatch +export const useAppDispatch: () => AppDispatch = useDispatch +export type RootState = ReturnType +export const useTypedSelector: TypedUseSelectorHook = useSelector diff --git a/examples/query/react/kitchen-sink/src/features/counter/Counter.module.css b/examples/query/react/kitchen-sink/src/features/counter/Counter.module.css index 7e60ca1c..f4689a5d 100644 --- a/examples/query/react/kitchen-sink/src/features/counter/Counter.module.css +++ b/examples/query/react/kitchen-sink/src/features/counter/Counter.module.css @@ -1,77 +1,77 @@ -.row { - display: flex; - align-items: center; - justify-content: center; -} - -.row:not(:last-child) { - margin-bottom: 16px; -} - -.value { - font-size: 78px; - padding-left: 16px; - padding-right: 16px; - margin-top: 2px; - font-family: 'Courier New', Courier, monospace; -} - -.button { - appearance: none; - background: none; - font-size: 32px; - padding-left: 12px; - padding-right: 12px; - outline: none; - border: 2px solid transparent; - color: rgb(112, 76, 182); - padding-bottom: 4px; - cursor: pointer; - background-color: rgba(112, 76, 182, 0.1); - border-radius: 2px; - transition: all 0.15s; -} - -.textbox { - font-size: 32px; - padding: 2px; - width: 64px; - text-align: center; - margin-right: 8px; -} - -.button:hover, -.button:focus { - border: 2px solid rgba(112, 76, 182, 0.4); -} - -.button:active { - background-color: rgba(112, 76, 182, 0.2); -} - -.asyncButton { - composes: button; - position: relative; - margin-left: 8px; -} - -.asyncButton:after { - content: ''; - background-color: rgba(112, 76, 182, 0.15); - display: block; - position: absolute; - width: 100%; - height: 100%; - left: 0; - top: 0; - opacity: 0; - transition: - width 1s linear, - opacity 0.5s ease 1s; -} - -.asyncButton:active:after { - width: 0%; - opacity: 1; - transition: 0s; -} +.row { + display: flex; + align-items: center; + justify-content: center; +} + +.row:not(:last-child) { + margin-bottom: 16px; +} + +.value { + font-size: 78px; + padding-left: 16px; + padding-right: 16px; + margin-top: 2px; + font-family: 'Courier New', Courier, monospace; +} + +.button { + appearance: none; + background: none; + font-size: 32px; + padding-left: 12px; + padding-right: 12px; + outline: none; + border: 2px solid transparent; + color: rgb(112, 76, 182); + padding-bottom: 4px; + cursor: pointer; + background-color: rgba(112, 76, 182, 0.1); + border-radius: 2px; + transition: all 0.15s; +} + +.textbox { + font-size: 32px; + padding: 2px; + width: 64px; + text-align: center; + margin-right: 8px; +} + +.button:hover, +.button:focus { + border: 2px solid rgba(112, 76, 182, 0.4); +} + +.button:active { + background-color: rgba(112, 76, 182, 0.2); +} + +.asyncButton { + composes: button; + position: relative; + margin-left: 8px; +} + +.asyncButton:after { + content: ''; + background-color: rgba(112, 76, 182, 0.15); + display: block; + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + opacity: 0; + transition: + width 1s linear, + opacity 0.5s ease 1s; +} + +.asyncButton:active:after { + width: 0%; + opacity: 1; + transition: 0s; +} diff --git a/examples/query/react/kitchen-sink/src/features/counter/Counter.tsx b/examples/query/react/kitchen-sink/src/features/counter/Counter.tsx index ed8c5bf3..02e22179 100644 --- a/examples/query/react/kitchen-sink/src/features/counter/Counter.tsx +++ b/examples/query/react/kitchen-sink/src/features/counter/Counter.tsx @@ -1,52 +1,52 @@ -import React, { useState } from 'react' -import styles from './Counter.module.css' -import { - useDecrementCountMutation, - useGetCountQuery, - useIncrementCountMutation, -} from '../../app/services/counter' - -export function Counter({ - id, - onRemove, -}: { - id?: string - onRemove?: () => void -}) { - const [pollingInterval, setPollingInterval] = useState(10000) - const { data } = useGetCountQuery(undefined, { pollingInterval }) - const [increment] = useIncrementCountMutation() - - const [decrement] = useDecrementCountMutation() - - return ( -
-
- - {data?.count || 0} - - - setPollingInterval(valueAsNumber) - } - /> - {onRemove && } -
-
- ) -} +import React, { useState } from 'react' +import styles from './Counter.module.css' +import { + useDecrementCountMutation, + useGetCountQuery, + useIncrementCountMutation, +} from '../../app/services/counter' + +export function Counter({ + id, + onRemove, +}: { + id?: string + onRemove?: () => void +}) { + const [pollingInterval, setPollingInterval] = useState(10000) + const { data } = useGetCountQuery(undefined, { pollingInterval }) + const [increment] = useIncrementCountMutation() + + const [decrement] = useDecrementCountMutation() + + return ( +
+
+ + {data?.count || 0} + + + setPollingInterval(valueAsNumber) + } + /> + {onRemove && } +
+
+ ) +} diff --git a/examples/query/react/kitchen-sink/src/features/posts/PostDetail.tsx b/examples/query/react/kitchen-sink/src/features/posts/PostDetail.tsx index 2ff9b8b1..efa40895 100644 --- a/examples/query/react/kitchen-sink/src/features/posts/PostDetail.tsx +++ b/examples/query/react/kitchen-sink/src/features/posts/PostDetail.tsx @@ -1,131 +1,131 @@ -import * as React from 'react' -import { useNavigate, useParams } from 'react-router-dom' -import { useTypedSelector } from '../../app/store' -import { - useDeletePostMutation, - useGetPostQuery, - useUpdatePostMutation, -} from '../../app/services/posts' -import { selectGlobalPollingEnabled } from '../polling/pollingSlice' - -const EditablePostName = ({ - name: initialName, - onUpdate, - onCancel, - loading = false, -}: { - name: string - onUpdate: (name: string) => void - onCancel: () => void - loading?: boolean -}) => { - const [name, setName] = React.useState(initialName) - - const handleChange = ({ - target: { value }, - }: React.ChangeEvent) => setName(value) - - const handleSubmit: React.FormEventHandler = (e) => { - e.preventDefault() - onUpdate(name) - } - const handleCancel = () => onCancel() - - return ( -
-
- - - -
-
- ) -} - -const PostJsonDetail = ({ id }: { id: number }) => { - const { data: post } = useGetPostQuery(id) - - return ( -
-
{JSON.stringify(post, null, 2)}
-
- ) -} - -export const PostDetail = () => { - const { id } = useParams<{ id: any }>() - const navigate = useNavigate() - const globalPolling = useTypedSelector(selectGlobalPollingEnabled) - - const [isEditing, setIsEditing] = React.useState(false) - - const { - data: post, - isFetching, - isLoading, - } = useGetPostQuery(id, { pollingInterval: globalPolling ? 3000 : 0 }) - - const [updatePost, { isLoading: isUpdating }] = useUpdatePostMutation() - const [deletePost, { isLoading: isDeleting }] = useDeletePostMutation() - - if (isLoading) { - return
Loading...
- } - - if (!post) { - return
Missing post!
- } - - return ( -
- {isEditing ? ( - - updatePost({ id, name }) - .then((result) => { - // handle the success! - console.log('Update Result', result) - setIsEditing(false) - }) - .catch((error) => console.error('Update Error', error)) - } - onCancel={() => setIsEditing(false)} - loading={isUpdating} - /> - ) : ( - -
-
-

- {post.name} {isFetching ? '...refetching' : ''} -

-
- - -
-
- )} - -
- ) -} +import * as React from 'react' +import { useNavigate, useParams } from 'react-router-dom' +import { useTypedSelector } from '../../app/store' +import { + useDeletePostMutation, + useGetPostQuery, + useUpdatePostMutation, +} from '../../app/services/posts' +import { selectGlobalPollingEnabled } from '../polling/pollingSlice' + +const EditablePostName = ({ + name: initialName, + onUpdate, + onCancel, + loading = false, +}: { + name: string + onUpdate: (name: string) => void + onCancel: () => void + loading?: boolean +}) => { + const [name, setName] = React.useState(initialName) + + const handleChange = ({ + target: { value }, + }: React.ChangeEvent) => setName(value) + + const handleSubmit: React.FormEventHandler = (e) => { + e.preventDefault() + onUpdate(name) + } + const handleCancel = () => onCancel() + + return ( +
+
+ + + +
+
+ ) +} + +const PostJsonDetail = ({ id }: { id: number }) => { + const { data: post } = useGetPostQuery(id) + + return ( +
+
{JSON.stringify(post, null, 2)}
+
+ ) +} + +export const PostDetail = () => { + const { id } = useParams<{ id: any }>() + const navigate = useNavigate() + const globalPolling = useTypedSelector(selectGlobalPollingEnabled) + + const [isEditing, setIsEditing] = React.useState(false) + + const { + data: post, + isFetching, + isLoading, + } = useGetPostQuery(id, { pollingInterval: globalPolling ? 3000 : 0 }) + + const [updatePost, { isLoading: isUpdating }] = useUpdatePostMutation() + const [deletePost, { isLoading: isDeleting }] = useDeletePostMutation() + + if (isLoading) { + return
Loading...
+ } + + if (!post) { + return
Missing post!
+ } + + return ( +
+ {isEditing ? ( + + updatePost({ id, name }) + .then((result) => { + // handle the success! + console.log('Update Result', result) + setIsEditing(false) + }) + .catch((error) => console.error('Update Error', error)) + } + onCancel={() => setIsEditing(false)} + loading={isUpdating} + /> + ) : ( + +
+
+

+ {post.name} {isFetching ? '...refetching' : ''} +

+
+ + +
+
+ )} + +
+ ) +} diff --git a/examples/query/react/kitchen-sink/src/features/posts/PostsManager.css b/examples/query/react/kitchen-sink/src/features/posts/PostsManager.css index 4f5c7da8..de5f5755 100644 --- a/examples/query/react/kitchen-sink/src/features/posts/PostsManager.css +++ b/examples/query/react/kitchen-sink/src/features/posts/PostsManager.css @@ -1,10 +1,10 @@ -.posts-list { - display: flex; - flex-direction: column; - flex-basis: 100%; - flex: 1; - min-height: 200px; - border-right: 1px solid #eee; - padding: 20px; - text-align: left; -} +.posts-list { + display: flex; + flex-direction: column; + flex-basis: 100%; + flex: 1; + min-height: 200px; + border-right: 1px solid #eee; + padding: 20px; + text-align: left; +} diff --git a/examples/query/react/kitchen-sink/src/features/posts/PostsManager.tsx b/examples/query/react/kitchen-sink/src/features/posts/PostsManager.tsx index cd717465..08aa899a 100644 --- a/examples/query/react/kitchen-sink/src/features/posts/PostsManager.tsx +++ b/examples/query/react/kitchen-sink/src/features/posts/PostsManager.tsx @@ -1,141 +1,141 @@ -import React, { useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { Route, Routes, useNavigate } from 'react-router-dom' -import { - Post, - useAddPostMutation, - useGetPostsQuery, - useLoginMutation, - useGetErrorProneQuery, -} from '../../app/services/posts' -import { selectIsAuthenticated, logout } from '../auth/authSlice' -import { PostDetail } from './PostDetail' -import './PostsManager.css' - -const AddPost = () => { - const initialValue = { name: '' } - const [post, setPost] = useState>(initialValue) - const [addPost, { isLoading }] = useAddPostMutation() - - const handleChange = ({ target }: React.ChangeEvent) => { - setPost((prev) => ({ - ...prev, - [target.name]: target.value, - })) - } - - const handleSubmit: React.FormEventHandler = async (e) => { - e.preventDefault() - await addPost(post) - setPost(initialValue) - } - - return ( -
-
-
- -
-
- -
-
-
- ) -} - -const PostListItem = ({ - data: { name, id }, - onSelect, -}: { - data: Post - onSelect: (id: number) => void -}) => { - return ( -
  • - onSelect(id)}> - {name} - -
  • - ) -} - -const PostList = () => { - const { data: posts, isLoading } = useGetPostsQuery() - const navigate = useNavigate() - - if (isLoading) { - return
    Loading
    - } - - if (!posts) { - return
    No posts :(
    - } - - return ( -
    - {posts.map((post) => ( - navigate(`/posts/${id}`)} - /> - ))} -
    - ) -} - -export const PostsManager = () => { - const [login] = useLoginMutation() - const [initRetries, setInitRetries] = useState(false) - const { data, error, isFetching } = useGetErrorProneQuery(undefined, { - skip: !initRetries, - }) - const dispatch = useDispatch() - const isAuthenticated = useSelector(selectIsAuthenticated) - - return ( -
    -

    Posts

    - {!isAuthenticated ? ( - - ) : ( - - )} - -
    -
    -
    - -
    - Posts: - -
    - List with duplicate subscription: - -
    -
    - - } /> - -
    -
    -
    - ) -} - -export default PostsManager +import React, { useState } from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { Route, Routes, useNavigate } from 'react-router-dom' +import { + Post, + useAddPostMutation, + useGetPostsQuery, + useLoginMutation, + useGetErrorProneQuery, +} from '../../app/services/posts' +import { selectIsAuthenticated, logout } from '../auth/authSlice' +import { PostDetail } from './PostDetail' +import './PostsManager.css' + +const AddPost = () => { + const initialValue = { name: '' } + const [post, setPost] = useState>(initialValue) + const [addPost, { isLoading }] = useAddPostMutation() + + const handleChange = ({ target }: React.ChangeEvent) => { + setPost((prev) => ({ + ...prev, + [target.name]: target.value, + })) + } + + const handleSubmit: React.FormEventHandler = async (e) => { + e.preventDefault() + await addPost(post) + setPost(initialValue) + } + + return ( +
    +
    +
    + +
    +
    + +
    +
    +
    + ) +} + +const PostListItem = ({ + data: { name, id }, + onSelect, +}: { + data: Post + onSelect: (id: number) => void +}) => { + return ( +
  • + onSelect(id)}> + {name} + +
  • + ) +} + +const PostList = () => { + const { data: posts, isLoading } = useGetPostsQuery() + const navigate = useNavigate() + + if (isLoading) { + return
    Loading
    + } + + if (!posts) { + return
    No posts :(
    + } + + return ( +
    + {posts.map((post) => ( + navigate(`/posts/${id}`)} + /> + ))} +
    + ) +} + +export const PostsManager = () => { + const [login] = useLoginMutation() + const [initRetries, setInitRetries] = useState(false) + const { data, error, isFetching } = useGetErrorProneQuery(undefined, { + skip: !initRetries, + }) + const dispatch = useDispatch() + const isAuthenticated = useSelector(selectIsAuthenticated) + + return ( +
    +

    Posts

    + {!isAuthenticated ? ( + + ) : ( + + )} + +
    +
    +
    + +
    + Posts: + +
    + List with duplicate subscription: + +
    +
    + + } /> + +
    +
    +
    + ) +} + +export default PostsManager diff --git a/examples/query/react/kitchen-sink/src/mocks/browser.ts b/examples/query/react/kitchen-sink/src/mocks/browser.ts index d8d2c7d7..b234fdae 100644 --- a/examples/query/react/kitchen-sink/src/mocks/browser.ts +++ b/examples/query/react/kitchen-sink/src/mocks/browser.ts @@ -1,4 +1,4 @@ -import { setupWorker } from 'msw' -import { handlers } from './handlers' - -export const worker = setupWorker(...handlers) +import { setupWorker } from 'msw' +import { handlers } from './handlers' + +export const worker = setupWorker(...handlers) diff --git a/examples/query/react/mutations/src/app/services/posts.ts b/examples/query/react/mutations/src/app/services/posts.ts index 563d00f3..d8f20762 100644 --- a/examples/query/react/mutations/src/app/services/posts.ts +++ b/examples/query/react/mutations/src/app/services/posts.ts @@ -1,62 +1,62 @@ -import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' - -export interface Post { - id: string - name: string -} - -type PostsResponse = Post[] - -export const api = createApi({ - baseQuery: fetchBaseQuery({ baseUrl: '/' }), - tagTypes: ['Post'], - endpoints: (build) => ({ - getPosts: build.query({ - query: () => 'posts', - providesTags: (result) => - result - ? [ - ...result.map(({ id }) => ({ type: 'Post' as const, id })), - { type: 'Post', id: 'LIST' }, - ] - : [{ type: 'Post', id: 'LIST' }], - }), - addPost: build.mutation>({ - query: (body) => ({ - url: `posts`, - method: 'POST', - body, - }), - invalidatesTags: [{ type: 'Post', id: 'LIST' }], - }), - getPost: build.query({ - query: (id) => `posts/${id}`, - providesTags: (result, error, id) => [{ type: 'Post', id }], - }), - updatePost: build.mutation & Partial>({ - query: ({ id, ...patch }) => ({ - url: `posts/${id}`, - method: 'PUT', - body: patch, - }), - invalidatesTags: (result, error, { id }) => [{ type: 'Post', id }], - }), - deletePost: build.mutation<{ success: boolean; id: number }, number>({ - query(id) { - return { - url: `posts/${id}`, - method: 'DELETE', - } - }, - invalidatesTags: (result, error, id) => [{ type: 'Post', id }], - }), - }), -}) - -export const { - useGetPostQuery, - useGetPostsQuery, - useAddPostMutation, - useUpdatePostMutation, - useDeletePostMutation, -} = api +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' + +export interface Post { + id: string + name: string +} + +type PostsResponse = Post[] + +export const api = createApi({ + baseQuery: fetchBaseQuery({ baseUrl: '/' }), + tagTypes: ['Post'], + endpoints: (build) => ({ + getPosts: build.query({ + query: () => 'posts', + providesTags: (result) => + result + ? [ + ...result.map(({ id }) => ({ type: 'Post' as const, id })), + { type: 'Post', id: 'LIST' }, + ] + : [{ type: 'Post', id: 'LIST' }], + }), + addPost: build.mutation>({ + query: (body) => ({ + url: `posts`, + method: 'POST', + body, + }), + invalidatesTags: [{ type: 'Post', id: 'LIST' }], + }), + getPost: build.query({ + query: (id) => `posts/${id}`, + providesTags: (result, error, id) => [{ type: 'Post', id }], + }), + updatePost: build.mutation & Partial>({ + query: ({ id, ...patch }) => ({ + url: `posts/${id}`, + method: 'PUT', + body: patch, + }), + invalidatesTags: (result, error, { id }) => [{ type: 'Post', id }], + }), + deletePost: build.mutation<{ success: boolean; id: number }, number>({ + query(id) { + return { + url: `posts/${id}`, + method: 'DELETE', + } + }, + invalidatesTags: (result, error, id) => [{ type: 'Post', id }], + }), + }), +}) + +export const { + useGetPostQuery, + useGetPostsQuery, + useAddPostMutation, + useUpdatePostMutation, + useDeletePostMutation, +} = api diff --git a/examples/query/react/mutations/src/features/posts/PostDetail.tsx b/examples/query/react/mutations/src/features/posts/PostDetail.tsx index 5a55f1b1..6786dec4 100644 --- a/examples/query/react/mutations/src/features/posts/PostDetail.tsx +++ b/examples/query/react/mutations/src/features/posts/PostDetail.tsx @@ -1,153 +1,153 @@ -import React, { useState } from 'react' -import { useNavigate, useParams } from 'react-router-dom' -import { - useDeletePostMutation, - useGetPostQuery, - useUpdatePostMutation, -} from '../../app/services/posts' -import { - Box, - Button, - Center, - CloseButton, - Flex, - Heading, - Input, - Spacer, - Stack, - useToast, -} from '@chakra-ui/react' - -const EditablePostName = ({ - name: initialName, - onUpdate, - onCancel, - isLoading = false, -}: { - name: string - onUpdate: (name: string) => void - onCancel: () => void - isLoading?: boolean -}) => { - const [name, setName] = useState(initialName) - - const handleChange = ({ - target: { value }, - }: React.ChangeEvent) => setName(value) - - const handleUpdate = () => onUpdate(name) - const handleCancel = () => onCancel() - - return ( - - - - - - - - - - - - - ) -} - -const PostJsonDetail = ({ id }: { id: string }) => { - const { data: post } = useGetPostQuery(id) - - return ( - -
    {JSON.stringify(post, null, 2)}
    -
    - ) -} - -export const PostDetail = () => { - const { id } = useParams<{ id: any }>() - const navigate = useNavigate() - - const toast = useToast() - - const [isEditing, setIsEditing] = useState(false) - - const { data: post, isLoading } = useGetPostQuery(id) - - const [updatePost, { isLoading: isUpdating }] = useUpdatePostMutation() - - const [deletePost, { isLoading: isDeleting }] = useDeletePostMutation() - - if (isLoading) { - return
    Loading...
    - } - - if (!post) { - return ( -
    - - Post {id} is missing! Try reloading or selecting another post... - -
    - ) - } - - return ( - - {isEditing ? ( - { - try { - await updatePost({ id, name }).unwrap() - } catch { - toast({ - title: 'An error occurred', - description: "We couldn't save your changes, try again!", - status: 'error', - duration: 2000, - isClosable: true, - }) - } finally { - setIsEditing(false) - } - }} - onCancel={() => setIsEditing(false)} - isLoading={isUpdating} - /> - ) : ( - - - {post.name} - - - - - - - - - - )} - - - ) -} +import React, { useState } from 'react' +import { useNavigate, useParams } from 'react-router-dom' +import { + useDeletePostMutation, + useGetPostQuery, + useUpdatePostMutation, +} from '../../app/services/posts' +import { + Box, + Button, + Center, + CloseButton, + Flex, + Heading, + Input, + Spacer, + Stack, + useToast, +} from '@chakra-ui/react' + +const EditablePostName = ({ + name: initialName, + onUpdate, + onCancel, + isLoading = false, +}: { + name: string + onUpdate: (name: string) => void + onCancel: () => void + isLoading?: boolean +}) => { + const [name, setName] = useState(initialName) + + const handleChange = ({ + target: { value }, + }: React.ChangeEvent) => setName(value) + + const handleUpdate = () => onUpdate(name) + const handleCancel = () => onCancel() + + return ( + + + + + + + + + + + + + ) +} + +const PostJsonDetail = ({ id }: { id: string }) => { + const { data: post } = useGetPostQuery(id) + + return ( + +
    {JSON.stringify(post, null, 2)}
    +
    + ) +} + +export const PostDetail = () => { + const { id } = useParams<{ id: any }>() + const navigate = useNavigate() + + const toast = useToast() + + const [isEditing, setIsEditing] = useState(false) + + const { data: post, isLoading } = useGetPostQuery(id) + + const [updatePost, { isLoading: isUpdating }] = useUpdatePostMutation() + + const [deletePost, { isLoading: isDeleting }] = useDeletePostMutation() + + if (isLoading) { + return
    Loading...
    + } + + if (!post) { + return ( +
    + + Post {id} is missing! Try reloading or selecting another post... + +
    + ) + } + + return ( + + {isEditing ? ( + { + try { + await updatePost({ id, name }).unwrap() + } catch { + toast({ + title: 'An error occurred', + description: "We couldn't save your changes, try again!", + status: 'error', + duration: 2000, + isClosable: true, + }) + } finally { + setIsEditing(false) + } + }} + onCancel={() => setIsEditing(false)} + isLoading={isUpdating} + /> + ) : ( + + + {post.name} + + + + + + + + + + )} + + + ) +} diff --git a/examples/query/react/mutations/src/features/posts/PostsManager.tsx b/examples/query/react/mutations/src/features/posts/PostsManager.tsx index e5bf8a28..279365ac 100644 --- a/examples/query/react/mutations/src/features/posts/PostsManager.tsx +++ b/examples/query/react/mutations/src/features/posts/PostsManager.tsx @@ -1,164 +1,164 @@ -import { - Box, - Button, - Center, - Divider, - Flex, - FormControl, - FormLabel, - Heading, - Input, - List, - ListIcon, - ListItem, - Spacer, - Stat, - StatLabel, - StatNumber, - useToast, -} from '@chakra-ui/react' -import { MdBook } from 'react-icons/md' -import React, { useState } from 'react' -import { Route, Routes, useNavigate } from 'react-router-dom' -import { - Post, - useAddPostMutation, - useGetPostsQuery, -} from '../../app/services/posts' -import { PostDetail } from './PostDetail' - -const AddPost = () => { - const initialValue = { name: '' } - const [post, setPost] = useState>(initialValue) - const [addPost, { isLoading }] = useAddPostMutation() - const toast = useToast() - - const handleChange = ({ target }: React.ChangeEvent) => { - setPost((prev) => ({ - ...prev, - [target.name]: target.value, - })) - } - - const handleAddPost = async () => { - try { - await addPost(post).unwrap() - setPost(initialValue) - } catch { - toast({ - title: 'An error occurred', - description: "We couldn't save your post, try again!", - status: 'error', - duration: 2000, - isClosable: true, - }) - } - } - - return ( - - - - Post name - - - - - - - - - ) -} - -const PostList = () => { - const { data: posts, isLoading } = useGetPostsQuery() - const navigate = useNavigate() - - if (isLoading) { - return
    Loading
    - } - - if (!posts) { - return
    No posts :(
    - } - - return ( - - {posts.map(({ id, name }) => ( - navigate(`/posts/${id}`)}> - {name} - - ))} - - ) -} - -export const PostsCountStat = () => { - const { data: posts } = useGetPostsQuery() - - if (!posts) return null - - return ( - - Active Posts - {posts?.length} - - ) -} - -export const PostsManager = () => { - return ( - - - - Manage Posts - - - - - - - - - - - - - Posts - - - - - - - - } /> - - Select a post to edit! - - } - /> - - - - - ) -} - -export default PostsManager +import { + Box, + Button, + Center, + Divider, + Flex, + FormControl, + FormLabel, + Heading, + Input, + List, + ListIcon, + ListItem, + Spacer, + Stat, + StatLabel, + StatNumber, + useToast, +} from '@chakra-ui/react' +import { MdBook } from 'react-icons/md' +import React, { useState } from 'react' +import { Route, Routes, useNavigate } from 'react-router-dom' +import { + Post, + useAddPostMutation, + useGetPostsQuery, +} from '../../app/services/posts' +import { PostDetail } from './PostDetail' + +const AddPost = () => { + const initialValue = { name: '' } + const [post, setPost] = useState>(initialValue) + const [addPost, { isLoading }] = useAddPostMutation() + const toast = useToast() + + const handleChange = ({ target }: React.ChangeEvent) => { + setPost((prev) => ({ + ...prev, + [target.name]: target.value, + })) + } + + const handleAddPost = async () => { + try { + await addPost(post).unwrap() + setPost(initialValue) + } catch { + toast({ + title: 'An error occurred', + description: "We couldn't save your post, try again!", + status: 'error', + duration: 2000, + isClosable: true, + }) + } + } + + return ( + + + + Post name + + + + + + + + + ) +} + +const PostList = () => { + const { data: posts, isLoading } = useGetPostsQuery() + const navigate = useNavigate() + + if (isLoading) { + return
    Loading
    + } + + if (!posts) { + return
    No posts :(
    + } + + return ( + + {posts.map(({ id, name }) => ( + navigate(`/posts/${id}`)}> + {name} + + ))} + + ) +} + +export const PostsCountStat = () => { + const { data: posts } = useGetPostsQuery() + + if (!posts) return null + + return ( + + Active Posts + {posts?.length} + + ) +} + +export const PostsManager = () => { + return ( + + + + Manage Posts + + + + + + + + + + + + + Posts + + + + + + + + } /> + + Select a post to edit! + + } + /> + + + + + ) +} + +export default PostsManager diff --git a/examples/query/react/mutations/src/mocks/browser.ts b/examples/query/react/mutations/src/mocks/browser.ts index 53e40204..deff8abe 100644 --- a/examples/query/react/mutations/src/mocks/browser.ts +++ b/examples/query/react/mutations/src/mocks/browser.ts @@ -1,4 +1,4 @@ -import { setupWorker } from 'msw' -import { handlers } from './db' - -export const worker = setupWorker(...(handlers as any)) +import { setupWorker } from 'msw' +import { handlers } from './db' + +export const worker = setupWorker(...(handlers as any)) diff --git a/examples/query/react/mutations/src/store.ts b/examples/query/react/mutations/src/store.ts index c9c7f400..d1f10df1 100644 --- a/examples/query/react/mutations/src/store.ts +++ b/examples/query/react/mutations/src/store.ts @@ -1,11 +1,11 @@ -import { configureStore } from '@reduxjs/toolkit' -import { api } from './app/services/posts' - -export const store = configureStore({ - reducer: { - [api.reducerPath]: api.reducer, - }, - // adding the api middleware enables caching, invalidation, polling and other features of `rtk-query` - middleware: (getDefaultMiddleware) => - getDefaultMiddleware().concat(api.middleware), -}) +import { configureStore } from '@reduxjs/toolkit' +import { api } from './app/services/posts' + +export const store = configureStore({ + reducer: { + [api.reducerPath]: api.reducer, + }, + // adding the api middleware enables caching, invalidation, polling and other features of `rtk-query` + middleware: (getDefaultMiddleware) => + getDefaultMiddleware().concat(api.middleware), +}) diff --git a/examples/query/react/optimistic-update/src/app/services/posts.ts b/examples/query/react/optimistic-update/src/app/services/posts.ts index 3033e3ad..cd26c2c9 100644 --- a/examples/query/react/optimistic-update/src/app/services/posts.ts +++ b/examples/query/react/optimistic-update/src/app/services/posts.ts @@ -1,74 +1,74 @@ -import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' - -export interface Post { - id: string - name: string -} - -type PostsResponse = Post[] - -export const api = createApi({ - baseQuery: fetchBaseQuery({ baseUrl: '/' }), - tagTypes: ['Post'], - endpoints: (build) => ({ - getPosts: build.query({ - query: () => 'posts', - providesTags: (result) => - result - ? [ - ...result.map(({ id }) => ({ type: 'Post' as const, id })), - { type: 'Post', id: 'LIST' }, - ] - : [{ type: 'Post', id: 'LIST' }], - }), - addPost: build.mutation>({ - query: (body) => ({ - url: `posts`, - method: 'POST', - body, - }), - invalidatesTags: [{ type: 'Post', id: 'LIST' }], - }), - getPost: build.query({ - query: (id) => `posts/${id}`, - providesTags: (result, error, id) => [{ type: 'Post', id }], - }), - updatePost: build.mutation & Partial>({ - query: ({ id, ...patch }) => ({ - url: `posts/${id}`, - method: 'PUT', - body: patch, - }), - async onQueryStarted({ id, ...patch }, { dispatch, queryFulfilled }) { - const patchResult = dispatch( - api.util.updateQueryData('getPost', id, (draft) => { - Object.assign(draft, patch) - }), - ) - try { - await queryFulfilled - } catch { - patchResult.undo() - } - }, - invalidatesTags: (result, error, { id }) => [{ type: 'Post', id }], - }), - deletePost: build.mutation<{ success: boolean; id: number }, number>({ - query(id) { - return { - url: `posts/${id}`, - method: 'DELETE', - } - }, - invalidatesTags: (result, error, id) => [{ type: 'Post', id }], - }), - }), -}) - -export const { - useGetPostQuery, - useGetPostsQuery, - useAddPostMutation, - useUpdatePostMutation, - useDeletePostMutation, -} = api +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' + +export interface Post { + id: string + name: string +} + +type PostsResponse = Post[] + +export const api = createApi({ + baseQuery: fetchBaseQuery({ baseUrl: '/' }), + tagTypes: ['Post'], + endpoints: (build) => ({ + getPosts: build.query({ + query: () => 'posts', + providesTags: (result) => + result + ? [ + ...result.map(({ id }) => ({ type: 'Post' as const, id })), + { type: 'Post', id: 'LIST' }, + ] + : [{ type: 'Post', id: 'LIST' }], + }), + addPost: build.mutation>({ + query: (body) => ({ + url: `posts`, + method: 'POST', + body, + }), + invalidatesTags: [{ type: 'Post', id: 'LIST' }], + }), + getPost: build.query({ + query: (id) => `posts/${id}`, + providesTags: (result, error, id) => [{ type: 'Post', id }], + }), + updatePost: build.mutation & Partial>({ + query: ({ id, ...patch }) => ({ + url: `posts/${id}`, + method: 'PUT', + body: patch, + }), + async onQueryStarted({ id, ...patch }, { dispatch, queryFulfilled }) { + const patchResult = dispatch( + api.util.updateQueryData('getPost', id, (draft) => { + Object.assign(draft, patch) + }), + ) + try { + await queryFulfilled + } catch { + patchResult.undo() + } + }, + invalidatesTags: (result, error, { id }) => [{ type: 'Post', id }], + }), + deletePost: build.mutation<{ success: boolean; id: number }, number>({ + query(id) { + return { + url: `posts/${id}`, + method: 'DELETE', + } + }, + invalidatesTags: (result, error, id) => [{ type: 'Post', id }], + }), + }), +}) + +export const { + useGetPostQuery, + useGetPostsQuery, + useAddPostMutation, + useUpdatePostMutation, + useDeletePostMutation, +} = api diff --git a/examples/query/react/optimistic-update/src/features/posts/PostDetail.tsx b/examples/query/react/optimistic-update/src/features/posts/PostDetail.tsx index 5a55f1b1..6786dec4 100644 --- a/examples/query/react/optimistic-update/src/features/posts/PostDetail.tsx +++ b/examples/query/react/optimistic-update/src/features/posts/PostDetail.tsx @@ -1,153 +1,153 @@ -import React, { useState } from 'react' -import { useNavigate, useParams } from 'react-router-dom' -import { - useDeletePostMutation, - useGetPostQuery, - useUpdatePostMutation, -} from '../../app/services/posts' -import { - Box, - Button, - Center, - CloseButton, - Flex, - Heading, - Input, - Spacer, - Stack, - useToast, -} from '@chakra-ui/react' - -const EditablePostName = ({ - name: initialName, - onUpdate, - onCancel, - isLoading = false, -}: { - name: string - onUpdate: (name: string) => void - onCancel: () => void - isLoading?: boolean -}) => { - const [name, setName] = useState(initialName) - - const handleChange = ({ - target: { value }, - }: React.ChangeEvent) => setName(value) - - const handleUpdate = () => onUpdate(name) - const handleCancel = () => onCancel() - - return ( - - - - - - - - - - - - - ) -} - -const PostJsonDetail = ({ id }: { id: string }) => { - const { data: post } = useGetPostQuery(id) - - return ( - -
    {JSON.stringify(post, null, 2)}
    -
    - ) -} - -export const PostDetail = () => { - const { id } = useParams<{ id: any }>() - const navigate = useNavigate() - - const toast = useToast() - - const [isEditing, setIsEditing] = useState(false) - - const { data: post, isLoading } = useGetPostQuery(id) - - const [updatePost, { isLoading: isUpdating }] = useUpdatePostMutation() - - const [deletePost, { isLoading: isDeleting }] = useDeletePostMutation() - - if (isLoading) { - return
    Loading...
    - } - - if (!post) { - return ( -
    - - Post {id} is missing! Try reloading or selecting another post... - -
    - ) - } - - return ( - - {isEditing ? ( - { - try { - await updatePost({ id, name }).unwrap() - } catch { - toast({ - title: 'An error occurred', - description: "We couldn't save your changes, try again!", - status: 'error', - duration: 2000, - isClosable: true, - }) - } finally { - setIsEditing(false) - } - }} - onCancel={() => setIsEditing(false)} - isLoading={isUpdating} - /> - ) : ( - - - {post.name} - - - - - - - - - - )} - - - ) -} +import React, { useState } from 'react' +import { useNavigate, useParams } from 'react-router-dom' +import { + useDeletePostMutation, + useGetPostQuery, + useUpdatePostMutation, +} from '../../app/services/posts' +import { + Box, + Button, + Center, + CloseButton, + Flex, + Heading, + Input, + Spacer, + Stack, + useToast, +} from '@chakra-ui/react' + +const EditablePostName = ({ + name: initialName, + onUpdate, + onCancel, + isLoading = false, +}: { + name: string + onUpdate: (name: string) => void + onCancel: () => void + isLoading?: boolean +}) => { + const [name, setName] = useState(initialName) + + const handleChange = ({ + target: { value }, + }: React.ChangeEvent) => setName(value) + + const handleUpdate = () => onUpdate(name) + const handleCancel = () => onCancel() + + return ( + + + + + + + + + + + + + ) +} + +const PostJsonDetail = ({ id }: { id: string }) => { + const { data: post } = useGetPostQuery(id) + + return ( + +
    {JSON.stringify(post, null, 2)}
    +
    + ) +} + +export const PostDetail = () => { + const { id } = useParams<{ id: any }>() + const navigate = useNavigate() + + const toast = useToast() + + const [isEditing, setIsEditing] = useState(false) + + const { data: post, isLoading } = useGetPostQuery(id) + + const [updatePost, { isLoading: isUpdating }] = useUpdatePostMutation() + + const [deletePost, { isLoading: isDeleting }] = useDeletePostMutation() + + if (isLoading) { + return
    Loading...
    + } + + if (!post) { + return ( +
    + + Post {id} is missing! Try reloading or selecting another post... + +
    + ) + } + + return ( + + {isEditing ? ( + { + try { + await updatePost({ id, name }).unwrap() + } catch { + toast({ + title: 'An error occurred', + description: "We couldn't save your changes, try again!", + status: 'error', + duration: 2000, + isClosable: true, + }) + } finally { + setIsEditing(false) + } + }} + onCancel={() => setIsEditing(false)} + isLoading={isUpdating} + /> + ) : ( + + + {post.name} + + + + + + + + + + )} + + + ) +} diff --git a/examples/query/react/optimistic-update/src/features/posts/PostsManager.tsx b/examples/query/react/optimistic-update/src/features/posts/PostsManager.tsx index 7718cc20..77e02a66 100644 --- a/examples/query/react/optimistic-update/src/features/posts/PostsManager.tsx +++ b/examples/query/react/optimistic-update/src/features/posts/PostsManager.tsx @@ -1,192 +1,192 @@ -import { - Box, - Button, - Center, - Divider, - Flex, - FormControl, - FormLabel, - Heading, - Input, - List, - ListIcon, - ListItem, - Spacer, - Stat, - StatLabel, - StatNumber, -} from '@chakra-ui/react' -import { MdBook } from 'react-icons/md' -import React, { useState } from 'react' -import { Route, Routes, useNavigate } from 'react-router-dom' -import { - Post, - useAddPostMutation, - useGetPostQuery, - useGetPostsQuery, -} from '../../app/services/posts' -import { PostDetail } from './PostDetail' -import { v4 as uuid } from 'uuid' - -const AddPost = () => { - const initialValue: Post = { id: uuid(), name: '' } - const [post, setPost] = useState(initialValue) - const [addPost, { isLoading }] = useAddPostMutation() - - const handleChange = ({ target }: React.ChangeEvent) => { - setPost((prev) => ({ - ...prev, - [target.name]: target.value, - })) - } - - const handleAddPost = () => addPost(post).then(() => setPost(initialValue)) - - return ( - - - - Post name - - - - - - - - - ) -} - -const PostList = () => { - const { data: posts, isLoading } = useGetPostsQuery() - const navigate = useNavigate() - - if (isLoading) { - return
    Loading
    - } - - if (!posts) { - return
    No posts :(
    - } - - return ( - - {posts.map(({ id, name }) => ( - navigate(`/posts/${id}`)}> - {name} - - ))} - - ) -} - -const PostNameSubscribed = ({ id }: { id: string }) => { - const { data, isFetching } = useGetPostQuery(id) - const navigate = useNavigate() - - console.log('data', data, isFetching) - - if (!data) return null - - return ( - navigate(`/posts/${id}`)}> - {data.name} - - ) -} -const PostListSubscribed = () => { - const { data: posts, isLoading } = useGetPostsQuery() - - if (isLoading) { - return
    Loading
    - } - - if (!posts) { - return
    No posts :(
    - } - - return ( - - {posts.map(({ id }) => ( - - ))} - - ) -} - -export const PostsCountStat = () => { - const { data: posts } = useGetPostsQuery() - - if (!posts) return null - - return ( - - Active Posts - {posts?.length} - - ) -} - -export const PostsManager = () => { - return ( - - - - Manage Posts - - - - - - - - - - - - - Posts - - - - - - Posts (subscribed) - - - - - - - - } /> - - Select a post to edit! - - } - /> - - - - - ) -} - -export default PostsManager +import { + Box, + Button, + Center, + Divider, + Flex, + FormControl, + FormLabel, + Heading, + Input, + List, + ListIcon, + ListItem, + Spacer, + Stat, + StatLabel, + StatNumber, +} from '@chakra-ui/react' +import { MdBook } from 'react-icons/md' +import React, { useState } from 'react' +import { Route, Routes, useNavigate } from 'react-router-dom' +import { + Post, + useAddPostMutation, + useGetPostQuery, + useGetPostsQuery, +} from '../../app/services/posts' +import { PostDetail } from './PostDetail' +import { v4 as uuid } from 'uuid' + +const AddPost = () => { + const initialValue: Post = { id: uuid(), name: '' } + const [post, setPost] = useState(initialValue) + const [addPost, { isLoading }] = useAddPostMutation() + + const handleChange = ({ target }: React.ChangeEvent) => { + setPost((prev) => ({ + ...prev, + [target.name]: target.value, + })) + } + + const handleAddPost = () => addPost(post).then(() => setPost(initialValue)) + + return ( + + + + Post name + + + + + + + + + ) +} + +const PostList = () => { + const { data: posts, isLoading } = useGetPostsQuery() + const navigate = useNavigate() + + if (isLoading) { + return
    Loading
    + } + + if (!posts) { + return
    No posts :(
    + } + + return ( + + {posts.map(({ id, name }) => ( + navigate(`/posts/${id}`)}> + {name} + + ))} + + ) +} + +const PostNameSubscribed = ({ id }: { id: string }) => { + const { data, isFetching } = useGetPostQuery(id) + const navigate = useNavigate() + + console.log('data', data, isFetching) + + if (!data) return null + + return ( + navigate(`/posts/${id}`)}> + {data.name} + + ) +} +const PostListSubscribed = () => { + const { data: posts, isLoading } = useGetPostsQuery() + + if (isLoading) { + return
    Loading
    + } + + if (!posts) { + return
    No posts :(
    + } + + return ( + + {posts.map(({ id }) => ( + + ))} + + ) +} + +export const PostsCountStat = () => { + const { data: posts } = useGetPostsQuery() + + if (!posts) return null + + return ( + + Active Posts + {posts?.length} + + ) +} + +export const PostsManager = () => { + return ( + + + + Manage Posts + + + + + + + + + + + + + Posts + + + + + + Posts (subscribed) + + + + + + + + } /> + + Select a post to edit! + + } + /> + + + + + ) +} + +export default PostsManager diff --git a/examples/query/react/optimistic-update/src/mocks/browser.ts b/examples/query/react/optimistic-update/src/mocks/browser.ts index 53e40204..deff8abe 100644 --- a/examples/query/react/optimistic-update/src/mocks/browser.ts +++ b/examples/query/react/optimistic-update/src/mocks/browser.ts @@ -1,4 +1,4 @@ -import { setupWorker } from 'msw' -import { handlers } from './db' - -export const worker = setupWorker(...(handlers as any)) +import { setupWorker } from 'msw' +import { handlers } from './db' + +export const worker = setupWorker(...(handlers as any)) diff --git a/examples/query/react/pagination/src/app/services/posts.ts b/examples/query/react/pagination/src/app/services/posts.ts index b4863852..a21040fc 100644 --- a/examples/query/react/pagination/src/app/services/posts.ts +++ b/examples/query/react/pagination/src/app/services/posts.ts @@ -1,31 +1,31 @@ -import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' - -export const postStatuses = ['draft', 'published', 'pending_review'] as const - -export interface Post { - id: string - title: string - author: string - content: string - status: (typeof postStatuses)[number] - created_at: string - updated_at: string -} - -interface ListResponse { - page: number - per_page: number - total: number - total_pages: number - data: T[] -} -export const api = createApi({ - baseQuery: fetchBaseQuery({ baseUrl: '/' }), - endpoints: (build) => ({ - listPosts: build.query, number | void>({ - query: (page = 1) => `posts?page=${page}`, - }), - }), -}) - -export const { useListPostsQuery } = api +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' + +export const postStatuses = ['draft', 'published', 'pending_review'] as const + +export interface Post { + id: string + title: string + author: string + content: string + status: (typeof postStatuses)[number] + created_at: string + updated_at: string +} + +interface ListResponse { + page: number + per_page: number + total: number + total_pages: number + data: T[] +} +export const api = createApi({ + baseQuery: fetchBaseQuery({ baseUrl: '/' }), + endpoints: (build) => ({ + listPosts: build.query, number | void>({ + query: (page = 1) => `posts?page=${page}`, + }), + }), +}) + +export const { useListPostsQuery } = api diff --git a/examples/query/react/pagination/src/features/posts/PostsManager.tsx b/examples/query/react/pagination/src/features/posts/PostsManager.tsx index 52f3b824..999b05b4 100644 --- a/examples/query/react/pagination/src/features/posts/PostsManager.tsx +++ b/examples/query/react/pagination/src/features/posts/PostsManager.tsx @@ -1,110 +1,110 @@ -import * as React from 'react' -import { - Badge, - Box, - Button, - Divider, - Flex, - Heading, - HStack, - Icon, - List, - ListIcon, - ListItem, - Spacer, - Stat, - StatLabel, - StatNumber, -} from '@chakra-ui/react' -import { MdArrowBack, MdArrowForward, MdBook } from 'react-icons/md' -import { Post, useListPostsQuery } from '../../app/services/posts' - -const getColorForStatus = (status: Post['status']) => { - return status === 'draft' - ? 'gray' - : status === 'pending_review' - ? 'orange' - : 'green' -} - -const PostList = () => { - const [page, setPage] = React.useState(1) - const { data: posts, isLoading, isFetching } = useListPostsQuery(page) - - if (isLoading) { - return
    Loading
    - } - - if (!posts?.data) { - return
    No posts :(
    - } - - return ( - - - - - {`${page} / ${posts.total_pages}`} - - - {posts?.data.map(({ id, title, status }) => ( - - {title}{' '} - - {status} - - - ))} - - - ) -} - -export const PostsCountStat = () => { - const { data: posts } = useListPostsQuery() - - return ( - - Total Posts - {`${posts?.total || 'NA'}`} - - ) -} - -export const PostsManager = () => { - return ( - - - - Manage Posts - - - - - - - - - - - - ) -} - -export default PostsManager +import * as React from 'react' +import { + Badge, + Box, + Button, + Divider, + Flex, + Heading, + HStack, + Icon, + List, + ListIcon, + ListItem, + Spacer, + Stat, + StatLabel, + StatNumber, +} from '@chakra-ui/react' +import { MdArrowBack, MdArrowForward, MdBook } from 'react-icons/md' +import { Post, useListPostsQuery } from '../../app/services/posts' + +const getColorForStatus = (status: Post['status']) => { + return status === 'draft' + ? 'gray' + : status === 'pending_review' + ? 'orange' + : 'green' +} + +const PostList = () => { + const [page, setPage] = React.useState(1) + const { data: posts, isLoading, isFetching } = useListPostsQuery(page) + + if (isLoading) { + return
    Loading
    + } + + if (!posts?.data) { + return
    No posts :(
    + } + + return ( + + + + + {`${page} / ${posts.total_pages}`} + + + {posts?.data.map(({ id, title, status }) => ( + + {title}{' '} + + {status} + + + ))} + + + ) +} + +export const PostsCountStat = () => { + const { data: posts } = useListPostsQuery() + + return ( + + Total Posts + {`${posts?.total || 'NA'}`} + + ) +} + +export const PostsManager = () => { + return ( + + + + Manage Posts + + + + + + + + + + + + ) +} + +export default PostsManager diff --git a/examples/query/react/pagination/src/mocks/browser.ts b/examples/query/react/pagination/src/mocks/browser.ts index 53e40204..deff8abe 100644 --- a/examples/query/react/pagination/src/mocks/browser.ts +++ b/examples/query/react/pagination/src/mocks/browser.ts @@ -1,4 +1,4 @@ -import { setupWorker } from 'msw' -import { handlers } from './db' - -export const worker = setupWorker(...(handlers as any)) +import { setupWorker } from 'msw' +import { handlers } from './db' + +export const worker = setupWorker(...(handlers as any)) diff --git a/examples/query/react/polling/src/services/pokemon.ts b/examples/query/react/polling/src/services/pokemon.ts index b338a20b..fb7cc5c1 100644 --- a/examples/query/react/polling/src/services/pokemon.ts +++ b/examples/query/react/polling/src/services/pokemon.ts @@ -1,15 +1,15 @@ -import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' -import type { PokemonName } from '../pokemon.data' - -export const pokemonApi = createApi({ - reducerPath: 'pokemonApi', - baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }), - endpoints: (builder) => ({ - getPokemonByName: builder.query({ - query: (name: PokemonName) => `pokemon/${name}`, - }), - }), -}) - -// Export hooks for usage in functional components -export const { useGetPokemonByNameQuery } = pokemonApi +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' +import type { PokemonName } from '../pokemon.data' + +export const pokemonApi = createApi({ + reducerPath: 'pokemonApi', + baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }), + endpoints: (builder) => ({ + getPokemonByName: builder.query({ + query: (name: PokemonName) => `pokemon/${name}`, + }), + }), +}) + +// Export hooks for usage in functional components +export const { useGetPokemonByNameQuery } = pokemonApi diff --git a/examples/query/react/polling/src/store.ts b/examples/query/react/polling/src/store.ts index 8168bb91..69b71824 100644 --- a/examples/query/react/polling/src/store.ts +++ b/examples/query/react/polling/src/store.ts @@ -1,11 +1,11 @@ -import { configureStore } from '@reduxjs/toolkit' -import { pokemonApi } from './services/pokemon' - -export const store = configureStore({ - reducer: { - [pokemonApi.reducerPath]: pokemonApi.reducer, - }, - // adding the api middleware enables caching, invalidation, polling and other features of `rtk-query` - middleware: (getDefaultMiddleware) => - getDefaultMiddleware().concat(pokemonApi.middleware), -}) +import { configureStore } from '@reduxjs/toolkit' +import { pokemonApi } from './services/pokemon' + +export const store = configureStore({ + reducer: { + [pokemonApi.reducerPath]: pokemonApi.reducer, + }, + // adding the api middleware enables caching, invalidation, polling and other features of `rtk-query` + middleware: (getDefaultMiddleware) => + getDefaultMiddleware().concat(pokemonApi.middleware), +}) diff --git a/examples/query/react/prefetching-automatic-waterfall/src/app/services/posts.ts b/examples/query/react/prefetching-automatic-waterfall/src/app/services/posts.ts index d4032ef9..2242d2e8 100644 --- a/examples/query/react/prefetching-automatic-waterfall/src/app/services/posts.ts +++ b/examples/query/react/prefetching-automatic-waterfall/src/app/services/posts.ts @@ -1,31 +1,31 @@ -import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' - -export const postStatuses = ['draft', 'published', 'pending_review'] as const - -export interface Post { - id: string - title: string - author: string - content: string - status: (typeof postStatuses)[number] - created_at: string - updated_at: string -} - -interface ListResponse { - page: number - per_page: number - total: number - total_pages: number - data: T[] -} -export const api = createApi({ - baseQuery: fetchBaseQuery({ baseUrl: '/' }), - endpoints: (build) => ({ - listPosts: build.query, number | void>({ - query: (page = 1) => `posts?page=${page}`, - }), - }), -}) - -export const { useListPostsQuery, usePrefetch } = api +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' + +export const postStatuses = ['draft', 'published', 'pending_review'] as const + +export interface Post { + id: string + title: string + author: string + content: string + status: (typeof postStatuses)[number] + created_at: string + updated_at: string +} + +interface ListResponse { + page: number + per_page: number + total: number + total_pages: number + data: T[] +} +export const api = createApi({ + baseQuery: fetchBaseQuery({ baseUrl: '/' }), + endpoints: (build) => ({ + listPosts: build.query, number | void>({ + query: (page = 1) => `posts?page=${page}`, + }), + }), +}) + +export const { useListPostsQuery, usePrefetch } = api diff --git a/examples/query/react/prefetching-automatic-waterfall/src/features/posts/PostsManager.tsx b/examples/query/react/prefetching-automatic-waterfall/src/features/posts/PostsManager.tsx index eb093dad..faf69b3f 100644 --- a/examples/query/react/prefetching-automatic-waterfall/src/features/posts/PostsManager.tsx +++ b/examples/query/react/prefetching-automatic-waterfall/src/features/posts/PostsManager.tsx @@ -1,130 +1,130 @@ -import { useCallback, useState, useEffect } from 'react' -import { - Badge, - Box, - Button, - Divider, - Flex, - Heading, - HStack, - Icon, - List, - ListIcon, - ListItem, - Spacer, - Stat, - StatLabel, - StatNumber, -} from '@chakra-ui/react' -import { MdArrowBack, MdArrowForward, MdBook } from 'react-icons/md' -import { Post, usePrefetch, useListPostsQuery } from '../../app/services/posts' - -const getColorForStatus = (status: Post['status']) => { - return status === 'draft' - ? 'gray' - : status === 'pending_review' - ? 'orange' - : 'green' -} - -const PostList = () => { - const [page, setPage] = useState(1) - const [hasPrefetchedAll, setHasPrefetchedAll] = useState(false) - - const { data: posts, isLoading, isFetching } = useListPostsQuery(page) - - const prefetchPage = usePrefetch('listPosts') - - const prefetchNext = useCallback(() => { - prefetchPage(page + 1) - }, [prefetchPage, page]) - - useEffect(() => { - if (!hasPrefetchedAll) { - if (posts && posts.total_pages > 1) { - ;[...new Array(posts.total_pages)].forEach((page, index) => { - if (index >= posts.total_pages) return - prefetchPage(index + 1, { force: true }) - }) - setHasPrefetchedAll(true) - } - } - }, [posts, page, prefetchPage, hasPrefetchedAll]) - - if (isLoading) { - return
    Loading
    - } - - if (!posts?.data) { - return
    No posts :(
    - } - - return ( - - - - - {`${page} / ${posts.total_pages}`} - - - {posts?.data.map(({ id, title, status }) => ( - - {title}{' '} - - {status} - - - ))} - - - ) -} -export const PostsCountStat = () => { - const { data: posts } = useListPostsQuery() - - return ( - - Total Posts - {`${posts?.total || 'NA'}`} - - ) -} - -export const PostsManager = () => { - return ( - - - - Manage Posts - - - - - - - - - - - - ) -} - -export default PostsManager +import { useCallback, useState, useEffect } from 'react' +import { + Badge, + Box, + Button, + Divider, + Flex, + Heading, + HStack, + Icon, + List, + ListIcon, + ListItem, + Spacer, + Stat, + StatLabel, + StatNumber, +} from '@chakra-ui/react' +import { MdArrowBack, MdArrowForward, MdBook } from 'react-icons/md' +import { Post, usePrefetch, useListPostsQuery } from '../../app/services/posts' + +const getColorForStatus = (status: Post['status']) => { + return status === 'draft' + ? 'gray' + : status === 'pending_review' + ? 'orange' + : 'green' +} + +const PostList = () => { + const [page, setPage] = useState(1) + const [hasPrefetchedAll, setHasPrefetchedAll] = useState(false) + + const { data: posts, isLoading, isFetching } = useListPostsQuery(page) + + const prefetchPage = usePrefetch('listPosts') + + const prefetchNext = useCallback(() => { + prefetchPage(page + 1) + }, [prefetchPage, page]) + + useEffect(() => { + if (!hasPrefetchedAll) { + if (posts && posts.total_pages > 1) { + ;[...new Array(posts.total_pages)].forEach((page, index) => { + if (index >= posts.total_pages) return + prefetchPage(index + 1, { force: true }) + }) + setHasPrefetchedAll(true) + } + } + }, [posts, page, prefetchPage, hasPrefetchedAll]) + + if (isLoading) { + return
    Loading
    + } + + if (!posts?.data) { + return
    No posts :(
    + } + + return ( + + + + + {`${page} / ${posts.total_pages}`} + + + {posts?.data.map(({ id, title, status }) => ( + + {title}{' '} + + {status} + + + ))} + + + ) +} +export const PostsCountStat = () => { + const { data: posts } = useListPostsQuery() + + return ( + + Total Posts + {`${posts?.total || 'NA'}`} + + ) +} + +export const PostsManager = () => { + return ( + + + + Manage Posts + + + + + + + + + + + + ) +} + +export default PostsManager diff --git a/examples/query/react/prefetching-automatic-waterfall/src/mocks/browser.ts b/examples/query/react/prefetching-automatic-waterfall/src/mocks/browser.ts index 53e40204..deff8abe 100644 --- a/examples/query/react/prefetching-automatic-waterfall/src/mocks/browser.ts +++ b/examples/query/react/prefetching-automatic-waterfall/src/mocks/browser.ts @@ -1,4 +1,4 @@ -import { setupWorker } from 'msw' -import { handlers } from './db' - -export const worker = setupWorker(...(handlers as any)) +import { setupWorker } from 'msw' +import { handlers } from './db' + +export const worker = setupWorker(...(handlers as any)) diff --git a/examples/query/react/prefetching-automatic/src/app/services/posts.ts b/examples/query/react/prefetching-automatic/src/app/services/posts.ts index d4032ef9..2242d2e8 100644 --- a/examples/query/react/prefetching-automatic/src/app/services/posts.ts +++ b/examples/query/react/prefetching-automatic/src/app/services/posts.ts @@ -1,31 +1,31 @@ -import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' - -export const postStatuses = ['draft', 'published', 'pending_review'] as const - -export interface Post { - id: string - title: string - author: string - content: string - status: (typeof postStatuses)[number] - created_at: string - updated_at: string -} - -interface ListResponse { - page: number - per_page: number - total: number - total_pages: number - data: T[] -} -export const api = createApi({ - baseQuery: fetchBaseQuery({ baseUrl: '/' }), - endpoints: (build) => ({ - listPosts: build.query, number | void>({ - query: (page = 1) => `posts?page=${page}`, - }), - }), -}) - -export const { useListPostsQuery, usePrefetch } = api +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' + +export const postStatuses = ['draft', 'published', 'pending_review'] as const + +export interface Post { + id: string + title: string + author: string + content: string + status: (typeof postStatuses)[number] + created_at: string + updated_at: string +} + +interface ListResponse { + page: number + per_page: number + total: number + total_pages: number + data: T[] +} +export const api = createApi({ + baseQuery: fetchBaseQuery({ baseUrl: '/' }), + endpoints: (build) => ({ + listPosts: build.query, number | void>({ + query: (page = 1) => `posts?page=${page}`, + }), + }), +}) + +export const { useListPostsQuery, usePrefetch } = api diff --git a/examples/query/react/prefetching-automatic/src/features/posts/PostsManager.tsx b/examples/query/react/prefetching-automatic/src/features/posts/PostsManager.tsx index 34354810..ed465054 100644 --- a/examples/query/react/prefetching-automatic/src/features/posts/PostsManager.tsx +++ b/examples/query/react/prefetching-automatic/src/features/posts/PostsManager.tsx @@ -1,122 +1,122 @@ -import { useCallback, useState, useEffect } from 'react' -import { - Badge, - Box, - Button, - Divider, - Flex, - Heading, - HStack, - Icon, - List, - ListIcon, - ListItem, - Spacer, - Stat, - StatLabel, - StatNumber, -} from '@chakra-ui/react' -import { MdArrowBack, MdArrowForward, MdBook } from 'react-icons/md' -import { Post, usePrefetch, useListPostsQuery } from '../../app/services/posts' - -const getColorForStatus = (status: Post['status']) => { - return status === 'draft' - ? 'gray' - : status === 'pending_review' - ? 'orange' - : 'green' -} - -const PostList = () => { - const [page, setPage] = useState(1) - const { data: posts, isLoading, isFetching } = useListPostsQuery(page) - const prefetchPage = usePrefetch('listPosts') - - const prefetchNext = useCallback(() => { - prefetchPage(page + 1) - }, [prefetchPage, page]) - - useEffect(() => { - if (page !== posts?.total_pages) { - prefetchNext() - } - }, [posts, page, prefetchNext]) - - if (isLoading) { - return
    Loading
    - } - - if (!posts?.data) { - return
    No posts :(
    - } - - return ( - - - - - {`${page} / ${posts.total_pages}`} - - - {posts?.data.map(({ id, title, status }) => ( - - {title}{' '} - - {status} - - - ))} - - - ) -} - -export const PostsCountStat = () => { - const { data: posts } = useListPostsQuery() - - return ( - - Total Posts - {`${posts?.total || 'NA'}`} - - ) -} - -export const PostsManager = () => { - return ( - - - - Manage Posts - - - - - - - - - - - - ) -} - -export default PostsManager +import { useCallback, useState, useEffect } from 'react' +import { + Badge, + Box, + Button, + Divider, + Flex, + Heading, + HStack, + Icon, + List, + ListIcon, + ListItem, + Spacer, + Stat, + StatLabel, + StatNumber, +} from '@chakra-ui/react' +import { MdArrowBack, MdArrowForward, MdBook } from 'react-icons/md' +import { Post, usePrefetch, useListPostsQuery } from '../../app/services/posts' + +const getColorForStatus = (status: Post['status']) => { + return status === 'draft' + ? 'gray' + : status === 'pending_review' + ? 'orange' + : 'green' +} + +const PostList = () => { + const [page, setPage] = useState(1) + const { data: posts, isLoading, isFetching } = useListPostsQuery(page) + const prefetchPage = usePrefetch('listPosts') + + const prefetchNext = useCallback(() => { + prefetchPage(page + 1) + }, [prefetchPage, page]) + + useEffect(() => { + if (page !== posts?.total_pages) { + prefetchNext() + } + }, [posts, page, prefetchNext]) + + if (isLoading) { + return
    Loading
    + } + + if (!posts?.data) { + return
    No posts :(
    + } + + return ( + + + + + {`${page} / ${posts.total_pages}`} + + + {posts?.data.map(({ id, title, status }) => ( + + {title}{' '} + + {status} + + + ))} + + + ) +} + +export const PostsCountStat = () => { + const { data: posts } = useListPostsQuery() + + return ( + + Total Posts + {`${posts?.total || 'NA'}`} + + ) +} + +export const PostsManager = () => { + return ( + + + + Manage Posts + + + + + + + + + + + + ) +} + +export default PostsManager diff --git a/examples/query/react/prefetching-automatic/src/mocks/browser.ts b/examples/query/react/prefetching-automatic/src/mocks/browser.ts index 53e40204..deff8abe 100644 --- a/examples/query/react/prefetching-automatic/src/mocks/browser.ts +++ b/examples/query/react/prefetching-automatic/src/mocks/browser.ts @@ -1,4 +1,4 @@ -import { setupWorker } from 'msw' -import { handlers } from './db' - -export const worker = setupWorker(...(handlers as any)) +import { setupWorker } from 'msw' +import { handlers } from './db' + +export const worker = setupWorker(...(handlers as any)) diff --git a/examples/query/react/prefetching/src/app/services/posts.ts b/examples/query/react/prefetching/src/app/services/posts.ts index d4032ef9..2242d2e8 100644 --- a/examples/query/react/prefetching/src/app/services/posts.ts +++ b/examples/query/react/prefetching/src/app/services/posts.ts @@ -1,31 +1,31 @@ -import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' - -export const postStatuses = ['draft', 'published', 'pending_review'] as const - -export interface Post { - id: string - title: string - author: string - content: string - status: (typeof postStatuses)[number] - created_at: string - updated_at: string -} - -interface ListResponse { - page: number - per_page: number - total: number - total_pages: number - data: T[] -} -export const api = createApi({ - baseQuery: fetchBaseQuery({ baseUrl: '/' }), - endpoints: (build) => ({ - listPosts: build.query, number | void>({ - query: (page = 1) => `posts?page=${page}`, - }), - }), -}) - -export const { useListPostsQuery, usePrefetch } = api +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' + +export const postStatuses = ['draft', 'published', 'pending_review'] as const + +export interface Post { + id: string + title: string + author: string + content: string + status: (typeof postStatuses)[number] + created_at: string + updated_at: string +} + +interface ListResponse { + page: number + per_page: number + total: number + total_pages: number + data: T[] +} +export const api = createApi({ + baseQuery: fetchBaseQuery({ baseUrl: '/' }), + endpoints: (build) => ({ + listPosts: build.query, number | void>({ + query: (page = 1) => `posts?page=${page}`, + }), + }), +}) + +export const { useListPostsQuery, usePrefetch } = api diff --git a/examples/query/react/prefetching/src/features/posts/PostsManager.tsx b/examples/query/react/prefetching/src/features/posts/PostsManager.tsx index 5999215a..4b9b8377 100644 --- a/examples/query/react/prefetching/src/features/posts/PostsManager.tsx +++ b/examples/query/react/prefetching/src/features/posts/PostsManager.tsx @@ -1,116 +1,116 @@ -import { useCallback, useState } from 'react' -import { - Badge, - Box, - Button, - Divider, - Flex, - Heading, - HStack, - Icon, - List, - ListIcon, - ListItem, - Spacer, - Stat, - StatLabel, - StatNumber, -} from '@chakra-ui/react' -import { MdArrowBack, MdArrowForward, MdBook } from 'react-icons/md' -import { Post, usePrefetch, useListPostsQuery } from '../../app/services/posts' - -const getColorForStatus = (status: Post['status']) => { - return status === 'draft' - ? 'gray' - : status === 'pending_review' - ? 'orange' - : 'green' -} - -const PostList = () => { - const [page, setPage] = useState(1) - const { data: posts, isLoading, isFetching } = useListPostsQuery(page) - const prefetchPage = usePrefetch('listPosts') - - const prefetchNext = useCallback(() => { - prefetchPage(page + 1) - }, [prefetchPage, page]) - - if (isLoading) { - return
    Loading
    - } - - if (!posts?.data) { - return
    No posts :(
    - } - - return ( - - - - - {`${page} / ${posts.total_pages}`} - - - {posts?.data.map(({ id, title, status }) => ( - - {title}{' '} - - {status} - - - ))} - - - ) -} - -export const PostsCountStat = () => { - const { data: posts } = useListPostsQuery() - - return ( - - Total Posts - {`${posts?.total || 'NA'}`} - - ) -} - -export const PostsManager = () => { - return ( - - - - Manage Posts - - - - - - - - - - - - ) -} - -export default PostsManager +import { useCallback, useState } from 'react' +import { + Badge, + Box, + Button, + Divider, + Flex, + Heading, + HStack, + Icon, + List, + ListIcon, + ListItem, + Spacer, + Stat, + StatLabel, + StatNumber, +} from '@chakra-ui/react' +import { MdArrowBack, MdArrowForward, MdBook } from 'react-icons/md' +import { Post, usePrefetch, useListPostsQuery } from '../../app/services/posts' + +const getColorForStatus = (status: Post['status']) => { + return status === 'draft' + ? 'gray' + : status === 'pending_review' + ? 'orange' + : 'green' +} + +const PostList = () => { + const [page, setPage] = useState(1) + const { data: posts, isLoading, isFetching } = useListPostsQuery(page) + const prefetchPage = usePrefetch('listPosts') + + const prefetchNext = useCallback(() => { + prefetchPage(page + 1) + }, [prefetchPage, page]) + + if (isLoading) { + return
    Loading
    + } + + if (!posts?.data) { + return
    No posts :(
    + } + + return ( + + + + + {`${page} / ${posts.total_pages}`} + + + {posts?.data.map(({ id, title, status }) => ( + + {title}{' '} + + {status} + + + ))} + + + ) +} + +export const PostsCountStat = () => { + const { data: posts } = useListPostsQuery() + + return ( + + Total Posts + {`${posts?.total || 'NA'}`} + + ) +} + +export const PostsManager = () => { + return ( + + + + Manage Posts + + + + + + + + + + + + ) +} + +export default PostsManager diff --git a/examples/query/react/prefetching/src/mocks/browser.ts b/examples/query/react/prefetching/src/mocks/browser.ts index 53e40204..deff8abe 100644 --- a/examples/query/react/prefetching/src/mocks/browser.ts +++ b/examples/query/react/prefetching/src/mocks/browser.ts @@ -1,4 +1,4 @@ -import { setupWorker } from 'msw' -import { handlers } from './db' - -export const worker = setupWorker(...(handlers as any)) +import { setupWorker } from 'msw' +import { handlers } from './db' + +export const worker = setupWorker(...(handlers as any)) diff --git a/packages/toolkit/api-extractor-react.json b/packages/toolkit/api-extractor-react.json index 5b110e70..35ed8b94 100644 --- a/packages/toolkit/api-extractor-react.json +++ b/packages/toolkit/api-extractor-react.json @@ -1,356 +1,356 @@ -/** - * Config file for API Extractor. For more info, please visit: https://api-extractor.com - */ -{ - "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - - /** - * Optionally specifies another JSON config file that this file extends from. This provides a way for - * standard settings to be shared across multiple projects. - * - * If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains - * the "extends" field. Otherwise, the first path segment is interpreted as an NPM package name, and will be - * resolved using NodeJS require(). - * - * SUPPORTED TOKENS: none - * DEFAULT VALUE: "" - */ - // "extends": "./shared/api-extractor-base.json" - // "extends": "my-package/include/api-extractor-base.json" - - /** - * Determines the "" token that can be used with other config file settings. The project folder - * typically contains the tsconfig.json and package.json config files, but the path is user-defined. - * - * The path is resolved relative to the folder of the config file that contains the setting. - * - * The default value for "projectFolder" is the token "", which means the folder is determined by traversing - * parent folders, starting from the folder containing api-extractor.json, and stopping at the first folder - * that contains a tsconfig.json file. If a tsconfig.json file cannot be found in this way, then an error - * will be reported. - * - * SUPPORTED TOKENS: - * DEFAULT VALUE: "" - */ - "projectFolder": ".", - - /** - * (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis. API Extractor - * analyzes the symbols exported by this module. - * - * The file extension must be ".d.ts" and not ".ts". - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - */ - "mainEntryPointFilePath": "dist/react/index.d.ts", - //"mainEntryPointFilePath": "/home/weber/tmp/rtk-origin-master/dist/index.d.ts", - - /** - * A list of NPM package names whose exports should be treated as part of this package. - * - * For example, suppose that Webpack is used to generate a distributed bundle for the project "library1", - * and another NPM package "library2" is embedded in this bundle. Some types from library2 may become part - * of the exported API for library1, but by default API Extractor would generate a .d.ts rollup that explicitly - * imports library2. To avoid this, we can specify: - * - * "bundledPackages": [ "library2" ], - * - * This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been - * local files for library1. - */ - "bundledPackages": [], - - /** - * Determines how the TypeScript compiler engine will be invoked by API Extractor. - */ - "compiler": { - /** - * Specifies the path to the tsconfig.json file to be used by API Extractor when analyzing the project. - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * Note: This setting will be ignored if "overrideTsconfig" is used. - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "/tsconfig.json" - */ - // "tsconfigFilePath": "/tsconfig.json", - /** - * Provides a compiler configuration that will be used instead of reading the tsconfig.json file from disk. - * The object must conform to the TypeScript tsconfig schema: - * - * http://json.schemastore.org/tsconfig - * - * If omitted, then the tsconfig.json file will be read from the "projectFolder". - * - * DEFAULT VALUE: no overrideTsconfig section - */ - // "overrideTsconfig": { - // . . . - // } - /** - * This option causes the compiler to be invoked with the --skipLibCheck option. This option is not recommended - * and may cause API Extractor to produce incomplete or incorrect declarations, but it may be required when - * dependencies contain declarations that are incompatible with the TypeScript engine that API Extractor uses - * for its analysis. Where possible, the underlying issue should be fixed rather than relying on skipLibCheck. - * - * DEFAULT VALUE: false - */ - // "skipLibCheck": true, - }, - - /** - * Configures how the API report file (*.api.md) will be generated. - */ - "apiReport": { - /** - * (REQUIRED) Whether to generate an API report. - */ - "enabled": true, - - /** - * The filename for the API report files. It will be combined with "reportFolder" or "reportTempFolder" to produce - * a full file path. - * - * The file extension should be ".api.md", and the string should not contain a path separator such as "\" or "/". - * - * SUPPORTED TOKENS: , - * DEFAULT VALUE: ".api.md" - */ - "reportFileName": "redux-toolkit-react.api.md", - - /** - * Specifies the folder where the API report file is written. The file name portion is determined by - * the "reportFileName" setting. - * - * The API report file is normally tracked by Git. Changes to it can be used to trigger a branch policy, - * e.g. for an API review. - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "/etc/" - */ - "reportFolder": "/etc/" - - /** - * Specifies the folder where the temporary report file is written. The file name portion is determined by - * the "reportFileName" setting. - * - * After the temporary file is written to disk, it is compared with the file in the "reportFolder". - * If they are different, a production build will fail. - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "/temp/" - */ - // "reportTempFolder": "/temp/" - }, - - /** - * Configures how the doc model file (*.api.json) will be generated. - */ - "docModel": { - /** - * (REQUIRED) Whether to generate a doc model file. - */ - "enabled": false - - /** - * The output path for the doc model file. The file extension should be ".api.json". - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "/temp/.api.json" - */ - // "apiJsonFilePath": "/temp/.api.json" - }, - - /** - * Configures how the .d.ts rollup file will be generated. - */ - "dtsRollup": { - /** - * (REQUIRED) Whether to generate the .d.ts rollup file. - */ - "enabled": false, - - /** - * Specifies the output path for a .d.ts rollup file to be generated without any trimming. - * This file will include all declarations that are exported by the main entry point. - * - * If the path is an empty string, then this file will not be written. - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "/dist/.d.ts" - */ - "untrimmedFilePath": "/dist/react/typings.d.ts" - - /** - * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "beta" release. - * This file will include only declarations that are marked as "@public" or "@beta". - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "" - */ - // "betaTrimmedFilePath": "/dist/-beta.d.ts", - - /** - * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "public" release. - * This file will include only declarations that are marked as "@public". - * - * If the path is an empty string, then this file will not be written. - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "" - */ - // "publicTrimmedFilePath": "/dist/-public.d.ts", - - /** - * When a declaration is trimmed, by default it will be replaced by a code comment such as - * "Excluded from this release type: exampleMember". Set "omitTrimmingComments" to true to remove the - * declaration completely. - * - * DEFAULT VALUE: false - */ - // "omitTrimmingComments": true - }, - - /** - * Configures how the tsdoc-metadata.json file will be generated. - */ - "tsdocMetadata": { - /** - * Whether to generate the tsdoc-metadata.json file. - * - * DEFAULT VALUE: true - */ - "enabled": false - - /** - * Specifies where the TSDoc metadata file should be written. - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * The default value is "", which causes the path to be automatically inferred from the "tsdocMetadata", - * "typings" or "main" fields of the project's package.json. If none of these fields are set, the lookup - * falls back to "tsdoc-metadata.json" in the package folder. - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "" - */ - // "tsdocMetadataFilePath": "/dist/tsdoc-metadata.json" - }, - - /** - * Configures how API Extractor reports error and warning messages produced during analysis. - * - * There are three sources of messages: compiler messages, API Extractor messages, and TSDoc messages. - */ - "messages": { - /** - * Configures handling of diagnostic messages reported by the TypeScript compiler engine while analyzing - * the input .d.ts files. - * - * TypeScript message identifiers start with "TS" followed by an integer. For example: "TS2551" - * - * DEFAULT VALUE: A single "default" entry with logLevel=warning. - */ - "compilerMessageReporting": { - /** - * Configures the default routing for messages that don't match an explicit rule in this table. - */ - "default": { - /** - * Specifies whether the message should be written to the the tool's output log. Note that - * the "addToApiReportFile" property may supersede this option. - * - * Possible values: "error", "warning", "none" - * - * Errors cause the build to fail and return a nonzero exit code. Warnings cause a production build fail - * and return a nonzero exit code. For a non-production build (e.g. when "api-extractor run" includes - * the "--local" option), the warning is displayed but the build will not fail. - * - * DEFAULT VALUE: "warning" - */ - "logLevel": "warning" - - /** - * When addToApiReportFile is true: If API Extractor is configured to write an API report file (.api.md), - * then the message will be written inside that file; otherwise, the message is instead logged according to - * the "logLevel" option. - * - * DEFAULT VALUE: false - */ - // "addToApiReportFile": false - } - - // "TS2551": { - // "logLevel": "warning", - // "addToApiReportFile": true - // }, - // - // . . . - }, - - /** - * Configures handling of messages reported by API Extractor during its analysis. - * - * API Extractor message identifiers start with "ae-". For example: "ae-extra-release-tag" - * - * DEFAULT VALUE: See api-extractor-defaults.json for the complete table of extractorMessageReporting mappings - */ - "extractorMessageReporting": { - "default": { - "logLevel": "warning" - // "addToApiReportFile": false - }, - "ae-forgotten-export": { - "logLevel": "none", - "addToApiReportFile": false - } - // - // . . . - }, - - /** - * Configures handling of messages reported by the TSDoc parser when analyzing code comments. - * - * TSDoc message identifiers start with "tsdoc-". For example: "tsdoc-link-tag-unescaped-text" - * - * DEFAULT VALUE: A single "default" entry with logLevel=warning. - */ - "tsdocMessageReporting": { - "default": { - "logLevel": "none" - // "addToApiReportFile": false - } - - // "tsdoc-link-tag-unescaped-text": { - // "logLevel": "warning", - // "addToApiReportFile": true - // }, - // - // . . . - } - } -} +/** + * Config file for API Extractor. For more info, please visit: https://api-extractor.com + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + + /** + * Optionally specifies another JSON config file that this file extends from. This provides a way for + * standard settings to be shared across multiple projects. + * + * If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains + * the "extends" field. Otherwise, the first path segment is interpreted as an NPM package name, and will be + * resolved using NodeJS require(). + * + * SUPPORTED TOKENS: none + * DEFAULT VALUE: "" + */ + // "extends": "./shared/api-extractor-base.json" + // "extends": "my-package/include/api-extractor-base.json" + + /** + * Determines the "" token that can be used with other config file settings. The project folder + * typically contains the tsconfig.json and package.json config files, but the path is user-defined. + * + * The path is resolved relative to the folder of the config file that contains the setting. + * + * The default value for "projectFolder" is the token "", which means the folder is determined by traversing + * parent folders, starting from the folder containing api-extractor.json, and stopping at the first folder + * that contains a tsconfig.json file. If a tsconfig.json file cannot be found in this way, then an error + * will be reported. + * + * SUPPORTED TOKENS: + * DEFAULT VALUE: "" + */ + "projectFolder": ".", + + /** + * (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis. API Extractor + * analyzes the symbols exported by this module. + * + * The file extension must be ".d.ts" and not ".ts". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + */ + "mainEntryPointFilePath": "dist/react/index.d.ts", + //"mainEntryPointFilePath": "/home/weber/tmp/rtk-origin-master/dist/index.d.ts", + + /** + * A list of NPM package names whose exports should be treated as part of this package. + * + * For example, suppose that Webpack is used to generate a distributed bundle for the project "library1", + * and another NPM package "library2" is embedded in this bundle. Some types from library2 may become part + * of the exported API for library1, but by default API Extractor would generate a .d.ts rollup that explicitly + * imports library2. To avoid this, we can specify: + * + * "bundledPackages": [ "library2" ], + * + * This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been + * local files for library1. + */ + "bundledPackages": [], + + /** + * Determines how the TypeScript compiler engine will be invoked by API Extractor. + */ + "compiler": { + /** + * Specifies the path to the tsconfig.json file to be used by API Extractor when analyzing the project. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * Note: This setting will be ignored if "overrideTsconfig" is used. + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/tsconfig.json" + */ + // "tsconfigFilePath": "/tsconfig.json", + /** + * Provides a compiler configuration that will be used instead of reading the tsconfig.json file from disk. + * The object must conform to the TypeScript tsconfig schema: + * + * http://json.schemastore.org/tsconfig + * + * If omitted, then the tsconfig.json file will be read from the "projectFolder". + * + * DEFAULT VALUE: no overrideTsconfig section + */ + // "overrideTsconfig": { + // . . . + // } + /** + * This option causes the compiler to be invoked with the --skipLibCheck option. This option is not recommended + * and may cause API Extractor to produce incomplete or incorrect declarations, but it may be required when + * dependencies contain declarations that are incompatible with the TypeScript engine that API Extractor uses + * for its analysis. Where possible, the underlying issue should be fixed rather than relying on skipLibCheck. + * + * DEFAULT VALUE: false + */ + // "skipLibCheck": true, + }, + + /** + * Configures how the API report file (*.api.md) will be generated. + */ + "apiReport": { + /** + * (REQUIRED) Whether to generate an API report. + */ + "enabled": true, + + /** + * The filename for the API report files. It will be combined with "reportFolder" or "reportTempFolder" to produce + * a full file path. + * + * The file extension should be ".api.md", and the string should not contain a path separator such as "\" or "/". + * + * SUPPORTED TOKENS: , + * DEFAULT VALUE: ".api.md" + */ + "reportFileName": "redux-toolkit-react.api.md", + + /** + * Specifies the folder where the API report file is written. The file name portion is determined by + * the "reportFileName" setting. + * + * The API report file is normally tracked by Git. Changes to it can be used to trigger a branch policy, + * e.g. for an API review. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/etc/" + */ + "reportFolder": "/etc/" + + /** + * Specifies the folder where the temporary report file is written. The file name portion is determined by + * the "reportFileName" setting. + * + * After the temporary file is written to disk, it is compared with the file in the "reportFolder". + * If they are different, a production build will fail. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/" + */ + // "reportTempFolder": "/temp/" + }, + + /** + * Configures how the doc model file (*.api.json) will be generated. + */ + "docModel": { + /** + * (REQUIRED) Whether to generate a doc model file. + */ + "enabled": false + + /** + * The output path for the doc model file. The file extension should be ".api.json". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/.api.json" + */ + // "apiJsonFilePath": "/temp/.api.json" + }, + + /** + * Configures how the .d.ts rollup file will be generated. + */ + "dtsRollup": { + /** + * (REQUIRED) Whether to generate the .d.ts rollup file. + */ + "enabled": false, + + /** + * Specifies the output path for a .d.ts rollup file to be generated without any trimming. + * This file will include all declarations that are exported by the main entry point. + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/dist/.d.ts" + */ + "untrimmedFilePath": "/dist/react/typings.d.ts" + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "beta" release. + * This file will include only declarations that are marked as "@public" or "@beta". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "betaTrimmedFilePath": "/dist/-beta.d.ts", + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "public" release. + * This file will include only declarations that are marked as "@public". + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "publicTrimmedFilePath": "/dist/-public.d.ts", + + /** + * When a declaration is trimmed, by default it will be replaced by a code comment such as + * "Excluded from this release type: exampleMember". Set "omitTrimmingComments" to true to remove the + * declaration completely. + * + * DEFAULT VALUE: false + */ + // "omitTrimmingComments": true + }, + + /** + * Configures how the tsdoc-metadata.json file will be generated. + */ + "tsdocMetadata": { + /** + * Whether to generate the tsdoc-metadata.json file. + * + * DEFAULT VALUE: true + */ + "enabled": false + + /** + * Specifies where the TSDoc metadata file should be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * The default value is "", which causes the path to be automatically inferred from the "tsdocMetadata", + * "typings" or "main" fields of the project's package.json. If none of these fields are set, the lookup + * falls back to "tsdoc-metadata.json" in the package folder. + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "tsdocMetadataFilePath": "/dist/tsdoc-metadata.json" + }, + + /** + * Configures how API Extractor reports error and warning messages produced during analysis. + * + * There are three sources of messages: compiler messages, API Extractor messages, and TSDoc messages. + */ + "messages": { + /** + * Configures handling of diagnostic messages reported by the TypeScript compiler engine while analyzing + * the input .d.ts files. + * + * TypeScript message identifiers start with "TS" followed by an integer. For example: "TS2551" + * + * DEFAULT VALUE: A single "default" entry with logLevel=warning. + */ + "compilerMessageReporting": { + /** + * Configures the default routing for messages that don't match an explicit rule in this table. + */ + "default": { + /** + * Specifies whether the message should be written to the the tool's output log. Note that + * the "addToApiReportFile" property may supersede this option. + * + * Possible values: "error", "warning", "none" + * + * Errors cause the build to fail and return a nonzero exit code. Warnings cause a production build fail + * and return a nonzero exit code. For a non-production build (e.g. when "api-extractor run" includes + * the "--local" option), the warning is displayed but the build will not fail. + * + * DEFAULT VALUE: "warning" + */ + "logLevel": "warning" + + /** + * When addToApiReportFile is true: If API Extractor is configured to write an API report file (.api.md), + * then the message will be written inside that file; otherwise, the message is instead logged according to + * the "logLevel" option. + * + * DEFAULT VALUE: false + */ + // "addToApiReportFile": false + } + + // "TS2551": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + }, + + /** + * Configures handling of messages reported by API Extractor during its analysis. + * + * API Extractor message identifiers start with "ae-". For example: "ae-extra-release-tag" + * + * DEFAULT VALUE: See api-extractor-defaults.json for the complete table of extractorMessageReporting mappings + */ + "extractorMessageReporting": { + "default": { + "logLevel": "warning" + // "addToApiReportFile": false + }, + "ae-forgotten-export": { + "logLevel": "none", + "addToApiReportFile": false + } + // + // . . . + }, + + /** + * Configures handling of messages reported by the TSDoc parser when analyzing code comments. + * + * TSDoc message identifiers start with "tsdoc-". For example: "tsdoc-link-tag-unescaped-text" + * + * DEFAULT VALUE: A single "default" entry with logLevel=warning. + */ + "tsdocMessageReporting": { + "default": { + "logLevel": "none" + // "addToApiReportFile": false + } + + // "tsdoc-link-tag-unescaped-text": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + } + } +} diff --git a/packages/toolkit/api-extractor.json b/packages/toolkit/api-extractor.json index bec61ea5..c9319953 100644 --- a/packages/toolkit/api-extractor.json +++ b/packages/toolkit/api-extractor.json @@ -1,356 +1,356 @@ -/** - * Config file for API Extractor. For more info, please visit: https://api-extractor.com - */ -{ - "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - - /** - * Optionally specifies another JSON config file that this file extends from. This provides a way for - * standard settings to be shared across multiple projects. - * - * If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains - * the "extends" field. Otherwise, the first path segment is interpreted as an NPM package name, and will be - * resolved using NodeJS require(). - * - * SUPPORTED TOKENS: none - * DEFAULT VALUE: "" - */ - // "extends": "./shared/api-extractor-base.json" - // "extends": "my-package/include/api-extractor-base.json" - - /** - * Determines the "" token that can be used with other config file settings. The project folder - * typically contains the tsconfig.json and package.json config files, but the path is user-defined. - * - * The path is resolved relative to the folder of the config file that contains the setting. - * - * The default value for "projectFolder" is the token "", which means the folder is determined by traversing - * parent folders, starting from the folder containing api-extractor.json, and stopping at the first folder - * that contains a tsconfig.json file. If a tsconfig.json file cannot be found in this way, then an error - * will be reported. - * - * SUPPORTED TOKENS: - * DEFAULT VALUE: "" - */ - "projectFolder": ".", - - /** - * (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis. API Extractor - * analyzes the symbols exported by this module. - * - * The file extension must be ".d.ts" and not ".ts". - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - */ - "mainEntryPointFilePath": "dist/index.d.ts", - //"mainEntryPointFilePath": "/home/weber/tmp/rtk-origin-master/dist/index.d.ts", - - /** - * A list of NPM package names whose exports should be treated as part of this package. - * - * For example, suppose that Webpack is used to generate a distributed bundle for the project "library1", - * and another NPM package "library2" is embedded in this bundle. Some types from library2 may become part - * of the exported API for library1, but by default API Extractor would generate a .d.ts rollup that explicitly - * imports library2. To avoid this, we can specify: - * - * "bundledPackages": [ "library2" ], - * - * This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been - * local files for library1. - */ - "bundledPackages": [], - - /** - * Determines how the TypeScript compiler engine will be invoked by API Extractor. - */ - "compiler": { - /** - * Specifies the path to the tsconfig.json file to be used by API Extractor when analyzing the project. - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * Note: This setting will be ignored if "overrideTsconfig" is used. - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "/tsconfig.json" - */ - // "tsconfigFilePath": "/tsconfig.json", - /** - * Provides a compiler configuration that will be used instead of reading the tsconfig.json file from disk. - * The object must conform to the TypeScript tsconfig schema: - * - * http://json.schemastore.org/tsconfig - * - * If omitted, then the tsconfig.json file will be read from the "projectFolder". - * - * DEFAULT VALUE: no overrideTsconfig section - */ - // "overrideTsconfig": { - // . . . - // } - /** - * This option causes the compiler to be invoked with the --skipLibCheck option. This option is not recommended - * and may cause API Extractor to produce incomplete or incorrect declarations, but it may be required when - * dependencies contain declarations that are incompatible with the TypeScript engine that API Extractor uses - * for its analysis. Where possible, the underlying issue should be fixed rather than relying on skipLibCheck. - * - * DEFAULT VALUE: false - */ - // "skipLibCheck": true, - }, - - /** - * Configures how the API report file (*.api.md) will be generated. - */ - "apiReport": { - /** - * (REQUIRED) Whether to generate an API report. - */ - "enabled": true, - - /** - * The filename for the API report files. It will be combined with "reportFolder" or "reportTempFolder" to produce - * a full file path. - * - * The file extension should be ".api.md", and the string should not contain a path separator such as "\" or "/". - * - * SUPPORTED TOKENS: , - * DEFAULT VALUE: ".api.md" - */ - "reportFileName": "redux-toolkit.api.md", - - /** - * Specifies the folder where the API report file is written. The file name portion is determined by - * the "reportFileName" setting. - * - * The API report file is normally tracked by Git. Changes to it can be used to trigger a branch policy, - * e.g. for an API review. - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "/etc/" - */ - "reportFolder": "/etc/" - - /** - * Specifies the folder where the temporary report file is written. The file name portion is determined by - * the "reportFileName" setting. - * - * After the temporary file is written to disk, it is compared with the file in the "reportFolder". - * If they are different, a production build will fail. - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "/temp/" - */ - // "reportTempFolder": "/temp/" - }, - - /** - * Configures how the doc model file (*.api.json) will be generated. - */ - "docModel": { - /** - * (REQUIRED) Whether to generate a doc model file. - */ - "enabled": false - - /** - * The output path for the doc model file. The file extension should be ".api.json". - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "/temp/.api.json" - */ - // "apiJsonFilePath": "/temp/.api.json" - }, - - /** - * Configures how the .d.ts rollup file will be generated. - */ - "dtsRollup": { - /** - * (REQUIRED) Whether to generate the .d.ts rollup file. - */ - "enabled": false, - - /** - * Specifies the output path for a .d.ts rollup file to be generated without any trimming. - * This file will include all declarations that are exported by the main entry point. - * - * If the path is an empty string, then this file will not be written. - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "/dist/.d.ts" - */ - "untrimmedFilePath": "/dist/typings.d.ts" - - /** - * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "beta" release. - * This file will include only declarations that are marked as "@public" or "@beta". - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "" - */ - // "betaTrimmedFilePath": "/dist/-beta.d.ts", - - /** - * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "public" release. - * This file will include only declarations that are marked as "@public". - * - * If the path is an empty string, then this file will not be written. - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "" - */ - // "publicTrimmedFilePath": "/dist/-public.d.ts", - - /** - * When a declaration is trimmed, by default it will be replaced by a code comment such as - * "Excluded from this release type: exampleMember". Set "omitTrimmingComments" to true to remove the - * declaration completely. - * - * DEFAULT VALUE: false - */ - // "omitTrimmingComments": true - }, - - /** - * Configures how the tsdoc-metadata.json file will be generated. - */ - "tsdocMetadata": { - /** - * Whether to generate the tsdoc-metadata.json file. - * - * DEFAULT VALUE: true - */ - "enabled": false - - /** - * Specifies where the TSDoc metadata file should be written. - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * The default value is "", which causes the path to be automatically inferred from the "tsdocMetadata", - * "typings" or "main" fields of the project's package.json. If none of these fields are set, the lookup - * falls back to "tsdoc-metadata.json" in the package folder. - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "" - */ - // "tsdocMetadataFilePath": "/dist/tsdoc-metadata.json" - }, - - /** - * Configures how API Extractor reports error and warning messages produced during analysis. - * - * There are three sources of messages: compiler messages, API Extractor messages, and TSDoc messages. - */ - "messages": { - /** - * Configures handling of diagnostic messages reported by the TypeScript compiler engine while analyzing - * the input .d.ts files. - * - * TypeScript message identifiers start with "TS" followed by an integer. For example: "TS2551" - * - * DEFAULT VALUE: A single "default" entry with logLevel=warning. - */ - "compilerMessageReporting": { - /** - * Configures the default routing for messages that don't match an explicit rule in this table. - */ - "default": { - /** - * Specifies whether the message should be written to the the tool's output log. Note that - * the "addToApiReportFile" property may supersede this option. - * - * Possible values: "error", "warning", "none" - * - * Errors cause the build to fail and return a nonzero exit code. Warnings cause a production build fail - * and return a nonzero exit code. For a non-production build (e.g. when "api-extractor run" includes - * the "--local" option), the warning is displayed but the build will not fail. - * - * DEFAULT VALUE: "warning" - */ - "logLevel": "warning" - - /** - * When addToApiReportFile is true: If API Extractor is configured to write an API report file (.api.md), - * then the message will be written inside that file; otherwise, the message is instead logged according to - * the "logLevel" option. - * - * DEFAULT VALUE: false - */ - // "addToApiReportFile": false - } - - // "TS2551": { - // "logLevel": "warning", - // "addToApiReportFile": true - // }, - // - // . . . - }, - - /** - * Configures handling of messages reported by API Extractor during its analysis. - * - * API Extractor message identifiers start with "ae-". For example: "ae-extra-release-tag" - * - * DEFAULT VALUE: See api-extractor-defaults.json for the complete table of extractorMessageReporting mappings - */ - "extractorMessageReporting": { - "default": { - "logLevel": "warning" - // "addToApiReportFile": false - }, - "ae-forgotten-export": { - "logLevel": "none", - "addToApiReportFile": false - } - // - // . . . - }, - - /** - * Configures handling of messages reported by the TSDoc parser when analyzing code comments. - * - * TSDoc message identifiers start with "tsdoc-". For example: "tsdoc-link-tag-unescaped-text" - * - * DEFAULT VALUE: A single "default" entry with logLevel=warning. - */ - "tsdocMessageReporting": { - "default": { - "logLevel": "none" - // "addToApiReportFile": false - } - - // "tsdoc-link-tag-unescaped-text": { - // "logLevel": "warning", - // "addToApiReportFile": true - // }, - // - // . . . - } - } -} +/** + * Config file for API Extractor. For more info, please visit: https://api-extractor.com + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + + /** + * Optionally specifies another JSON config file that this file extends from. This provides a way for + * standard settings to be shared across multiple projects. + * + * If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains + * the "extends" field. Otherwise, the first path segment is interpreted as an NPM package name, and will be + * resolved using NodeJS require(). + * + * SUPPORTED TOKENS: none + * DEFAULT VALUE: "" + */ + // "extends": "./shared/api-extractor-base.json" + // "extends": "my-package/include/api-extractor-base.json" + + /** + * Determines the "" token that can be used with other config file settings. The project folder + * typically contains the tsconfig.json and package.json config files, but the path is user-defined. + * + * The path is resolved relative to the folder of the config file that contains the setting. + * + * The default value for "projectFolder" is the token "", which means the folder is determined by traversing + * parent folders, starting from the folder containing api-extractor.json, and stopping at the first folder + * that contains a tsconfig.json file. If a tsconfig.json file cannot be found in this way, then an error + * will be reported. + * + * SUPPORTED TOKENS: + * DEFAULT VALUE: "" + */ + "projectFolder": ".", + + /** + * (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis. API Extractor + * analyzes the symbols exported by this module. + * + * The file extension must be ".d.ts" and not ".ts". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + */ + "mainEntryPointFilePath": "dist/index.d.ts", + //"mainEntryPointFilePath": "/home/weber/tmp/rtk-origin-master/dist/index.d.ts", + + /** + * A list of NPM package names whose exports should be treated as part of this package. + * + * For example, suppose that Webpack is used to generate a distributed bundle for the project "library1", + * and another NPM package "library2" is embedded in this bundle. Some types from library2 may become part + * of the exported API for library1, but by default API Extractor would generate a .d.ts rollup that explicitly + * imports library2. To avoid this, we can specify: + * + * "bundledPackages": [ "library2" ], + * + * This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been + * local files for library1. + */ + "bundledPackages": [], + + /** + * Determines how the TypeScript compiler engine will be invoked by API Extractor. + */ + "compiler": { + /** + * Specifies the path to the tsconfig.json file to be used by API Extractor when analyzing the project. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * Note: This setting will be ignored if "overrideTsconfig" is used. + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/tsconfig.json" + */ + // "tsconfigFilePath": "/tsconfig.json", + /** + * Provides a compiler configuration that will be used instead of reading the tsconfig.json file from disk. + * The object must conform to the TypeScript tsconfig schema: + * + * http://json.schemastore.org/tsconfig + * + * If omitted, then the tsconfig.json file will be read from the "projectFolder". + * + * DEFAULT VALUE: no overrideTsconfig section + */ + // "overrideTsconfig": { + // . . . + // } + /** + * This option causes the compiler to be invoked with the --skipLibCheck option. This option is not recommended + * and may cause API Extractor to produce incomplete or incorrect declarations, but it may be required when + * dependencies contain declarations that are incompatible with the TypeScript engine that API Extractor uses + * for its analysis. Where possible, the underlying issue should be fixed rather than relying on skipLibCheck. + * + * DEFAULT VALUE: false + */ + // "skipLibCheck": true, + }, + + /** + * Configures how the API report file (*.api.md) will be generated. + */ + "apiReport": { + /** + * (REQUIRED) Whether to generate an API report. + */ + "enabled": true, + + /** + * The filename for the API report files. It will be combined with "reportFolder" or "reportTempFolder" to produce + * a full file path. + * + * The file extension should be ".api.md", and the string should not contain a path separator such as "\" or "/". + * + * SUPPORTED TOKENS: , + * DEFAULT VALUE: ".api.md" + */ + "reportFileName": "redux-toolkit.api.md", + + /** + * Specifies the folder where the API report file is written. The file name portion is determined by + * the "reportFileName" setting. + * + * The API report file is normally tracked by Git. Changes to it can be used to trigger a branch policy, + * e.g. for an API review. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/etc/" + */ + "reportFolder": "/etc/" + + /** + * Specifies the folder where the temporary report file is written. The file name portion is determined by + * the "reportFileName" setting. + * + * After the temporary file is written to disk, it is compared with the file in the "reportFolder". + * If they are different, a production build will fail. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/" + */ + // "reportTempFolder": "/temp/" + }, + + /** + * Configures how the doc model file (*.api.json) will be generated. + */ + "docModel": { + /** + * (REQUIRED) Whether to generate a doc model file. + */ + "enabled": false + + /** + * The output path for the doc model file. The file extension should be ".api.json". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/.api.json" + */ + // "apiJsonFilePath": "/temp/.api.json" + }, + + /** + * Configures how the .d.ts rollup file will be generated. + */ + "dtsRollup": { + /** + * (REQUIRED) Whether to generate the .d.ts rollup file. + */ + "enabled": false, + + /** + * Specifies the output path for a .d.ts rollup file to be generated without any trimming. + * This file will include all declarations that are exported by the main entry point. + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/dist/.d.ts" + */ + "untrimmedFilePath": "/dist/typings.d.ts" + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "beta" release. + * This file will include only declarations that are marked as "@public" or "@beta". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "betaTrimmedFilePath": "/dist/-beta.d.ts", + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "public" release. + * This file will include only declarations that are marked as "@public". + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "publicTrimmedFilePath": "/dist/-public.d.ts", + + /** + * When a declaration is trimmed, by default it will be replaced by a code comment such as + * "Excluded from this release type: exampleMember". Set "omitTrimmingComments" to true to remove the + * declaration completely. + * + * DEFAULT VALUE: false + */ + // "omitTrimmingComments": true + }, + + /** + * Configures how the tsdoc-metadata.json file will be generated. + */ + "tsdocMetadata": { + /** + * Whether to generate the tsdoc-metadata.json file. + * + * DEFAULT VALUE: true + */ + "enabled": false + + /** + * Specifies where the TSDoc metadata file should be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * The default value is "", which causes the path to be automatically inferred from the "tsdocMetadata", + * "typings" or "main" fields of the project's package.json. If none of these fields are set, the lookup + * falls back to "tsdoc-metadata.json" in the package folder. + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "tsdocMetadataFilePath": "/dist/tsdoc-metadata.json" + }, + + /** + * Configures how API Extractor reports error and warning messages produced during analysis. + * + * There are three sources of messages: compiler messages, API Extractor messages, and TSDoc messages. + */ + "messages": { + /** + * Configures handling of diagnostic messages reported by the TypeScript compiler engine while analyzing + * the input .d.ts files. + * + * TypeScript message identifiers start with "TS" followed by an integer. For example: "TS2551" + * + * DEFAULT VALUE: A single "default" entry with logLevel=warning. + */ + "compilerMessageReporting": { + /** + * Configures the default routing for messages that don't match an explicit rule in this table. + */ + "default": { + /** + * Specifies whether the message should be written to the the tool's output log. Note that + * the "addToApiReportFile" property may supersede this option. + * + * Possible values: "error", "warning", "none" + * + * Errors cause the build to fail and return a nonzero exit code. Warnings cause a production build fail + * and return a nonzero exit code. For a non-production build (e.g. when "api-extractor run" includes + * the "--local" option), the warning is displayed but the build will not fail. + * + * DEFAULT VALUE: "warning" + */ + "logLevel": "warning" + + /** + * When addToApiReportFile is true: If API Extractor is configured to write an API report file (.api.md), + * then the message will be written inside that file; otherwise, the message is instead logged according to + * the "logLevel" option. + * + * DEFAULT VALUE: false + */ + // "addToApiReportFile": false + } + + // "TS2551": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + }, + + /** + * Configures handling of messages reported by API Extractor during its analysis. + * + * API Extractor message identifiers start with "ae-". For example: "ae-extra-release-tag" + * + * DEFAULT VALUE: See api-extractor-defaults.json for the complete table of extractorMessageReporting mappings + */ + "extractorMessageReporting": { + "default": { + "logLevel": "warning" + // "addToApiReportFile": false + }, + "ae-forgotten-export": { + "logLevel": "none", + "addToApiReportFile": false + } + // + // . . . + }, + + /** + * Configures handling of messages reported by the TSDoc parser when analyzing code comments. + * + * TSDoc message identifiers start with "tsdoc-". For example: "tsdoc-link-tag-unescaped-text" + * + * DEFAULT VALUE: A single "default" entry with logLevel=warning. + */ + "tsdocMessageReporting": { + "default": { + "logLevel": "none" + // "addToApiReportFile": false + } + + // "tsdoc-link-tag-unescaped-text": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + } + } +} diff --git a/packages/toolkit/src/entities/models.ts b/packages/toolkit/src/entities/models.ts index 76de9872..efe06450 100644 --- a/packages/toolkit/src/entities/models.ts +++ b/packages/toolkit/src/entities/models.ts @@ -1,198 +1,198 @@ -import type { UncheckedIndexedAccess } from '../uncheckedindexed' -import type { Draft } from 'immer' -import type { PayloadAction } from '../createAction' -import type { GetSelectorsOptions } from './state_selectors' -import type { CastAny, Id as Compute } from '../tsHelpers' - -/** - * @public - */ -export type EntityId = number | string - -/** - * @public - */ -export type Comparer = (a: T, b: T) => number - -/** - * @public - */ -export type IdSelector = (model: T) => Id - -/** - * @public - */ -export type Update = { id: Id; changes: Partial } - -/** - * @public - */ -export interface EntityState { - ids: Id[] - entities: Record -} - -/** - * @public - */ -export interface EntityAdapterOptions { - selectId?: IdSelector - sortComparer?: false | Comparer -} - -export type PreventAny = CastAny< - S, - EntityState -> - -export type DraftableEntityState = - | EntityState - | Draft> - -/** - * @public - */ -export interface EntityStateAdapter { - addOne>( - state: PreventAny, - entity: T, - ): S - addOne>( - state: PreventAny, - action: PayloadAction, - ): S - - addMany>( - state: PreventAny, - entities: readonly T[] | Record, - ): S - addMany>( - state: PreventAny, - entities: PayloadAction>, - ): S - - setOne>( - state: PreventAny, - entity: T, - ): S - setOne>( - state: PreventAny, - action: PayloadAction, - ): S - setMany>( - state: PreventAny, - entities: readonly T[] | Record, - ): S - setMany>( - state: PreventAny, - entities: PayloadAction>, - ): S - setAll>( - state: PreventAny, - entities: readonly T[] | Record, - ): S - setAll>( - state: PreventAny, - entities: PayloadAction>, - ): S - - removeOne>( - state: PreventAny, - key: Id, - ): S - removeOne>( - state: PreventAny, - key: PayloadAction, - ): S - - removeMany>( - state: PreventAny, - keys: readonly Id[], - ): S - removeMany>( - state: PreventAny, - keys: PayloadAction, - ): S - - removeAll>( - state: PreventAny, - ): S - - updateOne>( - state: PreventAny, - update: Update, - ): S - updateOne>( - state: PreventAny, - update: PayloadAction>, - ): S - - updateMany>( - state: PreventAny, - updates: ReadonlyArray>, - ): S - updateMany>( - state: PreventAny, - updates: PayloadAction>>, - ): S - - upsertOne>( - state: PreventAny, - entity: T, - ): S - upsertOne>( - state: PreventAny, - entity: PayloadAction, - ): S - - upsertMany>( - state: PreventAny, - entities: readonly T[] | Record, - ): S - upsertMany>( - state: PreventAny, - entities: PayloadAction>, - ): S -} - -/** - * @public - */ -export interface EntitySelectors { - selectIds: (state: V) => Id[] - selectEntities: (state: V) => Record - selectAll: (state: V) => T[] - selectTotal: (state: V) => number - selectById: (state: V, id: Id) => Compute> -} - -/** - * @public - */ -export interface EntityStateFactory { - getInitialState( - state?: undefined, - entities?: Record | readonly T[], - ): EntityState - getInitialState( - state: S, - entities?: Record | readonly T[], - ): EntityState & S -} - -/** - * @public - */ -export interface EntityAdapter - extends EntityStateAdapter, - EntityStateFactory, - Required> { - getSelectors( - selectState?: undefined, - options?: GetSelectorsOptions, - ): EntitySelectors, Id> - getSelectors( - selectState: (state: V) => EntityState, - options?: GetSelectorsOptions, - ): EntitySelectors -} +import type { UncheckedIndexedAccess } from '../uncheckedindexed' +import type { Draft } from 'immer' +import type { PayloadAction } from '../createAction' +import type { GetSelectorsOptions } from './state_selectors' +import type { CastAny, Id as Compute } from '../tsHelpers' + +/** + * @public + */ +export type EntityId = number | string + +/** + * @public + */ +export type Comparer = (a: T, b: T) => number + +/** + * @public + */ +export type IdSelector = (model: T) => Id + +/** + * @public + */ +export type Update = { id: Id; changes: Partial } + +/** + * @public + */ +export interface EntityState { + ids: Id[] + entities: Record +} + +/** + * @public + */ +export interface EntityAdapterOptions { + selectId?: IdSelector + sortComparer?: false | Comparer +} + +export type PreventAny = CastAny< + S, + EntityState +> + +export type DraftableEntityState = + | EntityState + | Draft> + +/** + * @public + */ +export interface EntityStateAdapter { + addOne>( + state: PreventAny, + entity: T, + ): S + addOne>( + state: PreventAny, + action: PayloadAction, + ): S + + addMany>( + state: PreventAny, + entities: readonly T[] | Record, + ): S + addMany>( + state: PreventAny, + entities: PayloadAction>, + ): S + + setOne>( + state: PreventAny, + entity: T, + ): S + setOne>( + state: PreventAny, + action: PayloadAction, + ): S + setMany>( + state: PreventAny, + entities: readonly T[] | Record, + ): S + setMany>( + state: PreventAny, + entities: PayloadAction>, + ): S + setAll>( + state: PreventAny, + entities: readonly T[] | Record, + ): S + setAll>( + state: PreventAny, + entities: PayloadAction>, + ): S + + removeOne>( + state: PreventAny, + key: Id, + ): S + removeOne>( + state: PreventAny, + key: PayloadAction, + ): S + + removeMany>( + state: PreventAny, + keys: readonly Id[], + ): S + removeMany>( + state: PreventAny, + keys: PayloadAction, + ): S + + removeAll>( + state: PreventAny, + ): S + + updateOne>( + state: PreventAny, + update: Update, + ): S + updateOne>( + state: PreventAny, + update: PayloadAction>, + ): S + + updateMany>( + state: PreventAny, + updates: ReadonlyArray>, + ): S + updateMany>( + state: PreventAny, + updates: PayloadAction>>, + ): S + + upsertOne>( + state: PreventAny, + entity: T, + ): S + upsertOne>( + state: PreventAny, + entity: PayloadAction, + ): S + + upsertMany>( + state: PreventAny, + entities: readonly T[] | Record, + ): S + upsertMany>( + state: PreventAny, + entities: PayloadAction>, + ): S +} + +/** + * @public + */ +export interface EntitySelectors { + selectIds: (state: V) => Id[] + selectEntities: (state: V) => Record + selectAll: (state: V) => T[] + selectTotal: (state: V) => number + selectById: (state: V, id: Id) => Compute> +} + +/** + * @public + */ +export interface EntityStateFactory { + getInitialState( + state?: undefined, + entities?: Record | readonly T[], + ): EntityState + getInitialState( + state: S, + entities?: Record | readonly T[], + ): EntityState & S +} + +/** + * @public + */ +export interface EntityAdapter + extends EntityStateAdapter, + EntityStateFactory, + Required> { + getSelectors( + selectState?: undefined, + options?: GetSelectorsOptions, + ): EntitySelectors, Id> + getSelectors( + selectState: (state: V) => EntityState, + options?: GetSelectorsOptions, + ): EntitySelectors +} diff --git a/packages/toolkit/src/entities/sorted_state_adapter.ts b/packages/toolkit/src/entities/sorted_state_adapter.ts index 1a2af5c5..dc730145 100644 --- a/packages/toolkit/src/entities/sorted_state_adapter.ts +++ b/packages/toolkit/src/entities/sorted_state_adapter.ts @@ -1,168 +1,168 @@ -import type { - IdSelector, - Comparer, - EntityStateAdapter, - Update, - EntityId, - DraftableEntityState, -} from './models' -import { createStateOperator } from './state_adapter' -import { createUnsortedStateAdapter } from './unsorted_state_adapter' -import { - selectIdValue, - ensureEntitiesArray, - splitAddedUpdatedEntities, -} from './utils' - -export function createSortedStateAdapter( - selectId: IdSelector, - sort: Comparer, -): EntityStateAdapter { - type R = DraftableEntityState - - const { removeOne, removeMany, removeAll } = - createUnsortedStateAdapter(selectId) - - function addOneMutably(entity: T, state: R): void { - return addManyMutably([entity], state) - } - - function addManyMutably( - newEntities: readonly T[] | Record, - state: R, - ): void { - newEntities = ensureEntitiesArray(newEntities) - - const models = newEntities.filter( - (model) => !(selectIdValue(model, selectId) in state.entities), - ) - - if (models.length !== 0) { - merge(models, state) - } - } - - function setOneMutably(entity: T, state: R): void { - return setManyMutably([entity], state) - } - - function setManyMutably( - newEntities: readonly T[] | Record, - state: R, - ): void { - newEntities = ensureEntitiesArray(newEntities) - if (newEntities.length !== 0) { - merge(newEntities, state) - } - } - - function setAllMutably( - newEntities: readonly T[] | Record, - state: R, - ): void { - newEntities = ensureEntitiesArray(newEntities) - state.entities = {} as Record - state.ids = [] - - addManyMutably(newEntities, state) - } - - function updateOneMutably(update: Update, state: R): void { - return updateManyMutably([update], state) - } - - function updateManyMutably( - updates: ReadonlyArray>, - state: R, - ): void { - let appliedUpdates = false - - for (let update of updates) { - const entity: T | undefined = (state.entities as Record)[update.id] - if (!entity) { - continue - } - - appliedUpdates = true - - Object.assign(entity, update.changes) - const newId = selectId(entity) - if (update.id !== newId) { - delete (state.entities as Record)[update.id] - ;(state.entities as Record)[newId] = entity - } - } - - if (appliedUpdates) { - resortEntities(state) - } - } - - function upsertOneMutably(entity: T, state: R): void { - return upsertManyMutably([entity], state) - } - - function upsertManyMutably( - newEntities: readonly T[] | Record, - state: R, - ): void { - const [added, updated] = splitAddedUpdatedEntities( - newEntities, - selectId, - state, - ) - - updateManyMutably(updated, state) - addManyMutably(added, state) - } - - function areArraysEqual(a: readonly unknown[], b: readonly unknown[]) { - if (a.length !== b.length) { - return false - } - - for (let i = 0; i < a.length && i < b.length; i++) { - if (a[i] === b[i]) { - continue - } - return false - } - return true - } - - function merge(models: readonly T[], state: R): void { - // Insert/overwrite all new/updated - models.forEach((model) => { - ;(state.entities as Record)[selectId(model)] = model - }) - - resortEntities(state) - } - - function resortEntities(state: R) { - const allEntities = Object.values(state.entities) as T[] - allEntities.sort(sort) - - const newSortedIds = allEntities.map(selectId) - const { ids } = state - - if (!areArraysEqual(ids, newSortedIds)) { - state.ids = newSortedIds - } - } - - return { - removeOne, - removeMany, - removeAll, - addOne: createStateOperator(addOneMutably), - updateOne: createStateOperator(updateOneMutably), - upsertOne: createStateOperator(upsertOneMutably), - setOne: createStateOperator(setOneMutably), - setMany: createStateOperator(setManyMutably), - setAll: createStateOperator(setAllMutably), - addMany: createStateOperator(addManyMutably), - updateMany: createStateOperator(updateManyMutably), - upsertMany: createStateOperator(upsertManyMutably), - } -} +import type { + IdSelector, + Comparer, + EntityStateAdapter, + Update, + EntityId, + DraftableEntityState, +} from './models' +import { createStateOperator } from './state_adapter' +import { createUnsortedStateAdapter } from './unsorted_state_adapter' +import { + selectIdValue, + ensureEntitiesArray, + splitAddedUpdatedEntities, +} from './utils' + +export function createSortedStateAdapter( + selectId: IdSelector, + sort: Comparer, +): EntityStateAdapter { + type R = DraftableEntityState + + const { removeOne, removeMany, removeAll } = + createUnsortedStateAdapter(selectId) + + function addOneMutably(entity: T, state: R): void { + return addManyMutably([entity], state) + } + + function addManyMutably( + newEntities: readonly T[] | Record, + state: R, + ): void { + newEntities = ensureEntitiesArray(newEntities) + + const models = newEntities.filter( + (model) => !(selectIdValue(model, selectId) in state.entities), + ) + + if (models.length !== 0) { + merge(models, state) + } + } + + function setOneMutably(entity: T, state: R): void { + return setManyMutably([entity], state) + } + + function setManyMutably( + newEntities: readonly T[] | Record, + state: R, + ): void { + newEntities = ensureEntitiesArray(newEntities) + if (newEntities.length !== 0) { + merge(newEntities, state) + } + } + + function setAllMutably( + newEntities: readonly T[] | Record, + state: R, + ): void { + newEntities = ensureEntitiesArray(newEntities) + state.entities = {} as Record + state.ids = [] + + addManyMutably(newEntities, state) + } + + function updateOneMutably(update: Update, state: R): void { + return updateManyMutably([update], state) + } + + function updateManyMutably( + updates: ReadonlyArray>, + state: R, + ): void { + let appliedUpdates = false + + for (let update of updates) { + const entity: T | undefined = (state.entities as Record)[update.id] + if (!entity) { + continue + } + + appliedUpdates = true + + Object.assign(entity, update.changes) + const newId = selectId(entity) + if (update.id !== newId) { + delete (state.entities as Record)[update.id] + ;(state.entities as Record)[newId] = entity + } + } + + if (appliedUpdates) { + resortEntities(state) + } + } + + function upsertOneMutably(entity: T, state: R): void { + return upsertManyMutably([entity], state) + } + + function upsertManyMutably( + newEntities: readonly T[] | Record, + state: R, + ): void { + const [added, updated] = splitAddedUpdatedEntities( + newEntities, + selectId, + state, + ) + + updateManyMutably(updated, state) + addManyMutably(added, state) + } + + function areArraysEqual(a: readonly unknown[], b: readonly unknown[]) { + if (a.length !== b.length) { + return false + } + + for (let i = 0; i < a.length && i < b.length; i++) { + if (a[i] === b[i]) { + continue + } + return false + } + return true + } + + function merge(models: readonly T[], state: R): void { + // Insert/overwrite all new/updated + models.forEach((model) => { + ;(state.entities as Record)[selectId(model)] = model + }) + + resortEntities(state) + } + + function resortEntities(state: R) { + const allEntities = Object.values(state.entities) as T[] + allEntities.sort(sort) + + const newSortedIds = allEntities.map(selectId) + const { ids } = state + + if (!areArraysEqual(ids, newSortedIds)) { + state.ids = newSortedIds + } + } + + return { + removeOne, + removeMany, + removeAll, + addOne: createStateOperator(addOneMutably), + updateOne: createStateOperator(updateOneMutably), + upsertOne: createStateOperator(upsertOneMutably), + setOne: createStateOperator(setOneMutably), + setMany: createStateOperator(setManyMutably), + setAll: createStateOperator(setAllMutably), + addMany: createStateOperator(addManyMutably), + updateMany: createStateOperator(updateManyMutably), + upsertMany: createStateOperator(upsertManyMutably), + } +} diff --git a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts index 0842a205..f6b0c7bd 100644 --- a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts +++ b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts @@ -1,59 +1,59 @@ -import { createEntityAdapter, createSlice } from '../..' -import type { - PayloadAction, - Slice, - SliceCaseReducers, - UnknownAction, -} from '../..' -import type { EntityId, EntityState, IdSelector } from '../models' -import type { BookModel } from './fixtures/book' - -describe('Entity Slice Enhancer', () => { - let slice: Slice> - - beforeEach(() => { - const indieSlice = entitySliceEnhancer({ - name: 'book', - selectId: (book: BookModel) => book.id, - }) - slice = indieSlice - }) - - it('exposes oneAdded', () => { - const book = { - id: '0', - title: 'Der Steppenwolf', - author: 'Herman Hesse', - } - const action = slice.actions.oneAdded(book) - const oneAdded = slice.reducer(undefined, action as UnknownAction) - expect(oneAdded.entities['0']).toBe(book) - }) -}) - -interface EntitySliceArgs { - name: string - selectId: IdSelector - modelReducer?: SliceCaseReducers -} - -function entitySliceEnhancer({ - name, - selectId, - modelReducer, -}: EntitySliceArgs) { - const modelAdapter = createEntityAdapter({ - selectId, - }) - - return createSlice({ - name, - initialState: modelAdapter.getInitialState(), - reducers: { - oneAdded(state, action: PayloadAction) { - modelAdapter.addOne(state, action.payload) - }, - ...modelReducer, - }, - }) -} +import { createEntityAdapter, createSlice } from '../..' +import type { + PayloadAction, + Slice, + SliceCaseReducers, + UnknownAction, +} from '../..' +import type { EntityId, EntityState, IdSelector } from '../models' +import type { BookModel } from './fixtures/book' + +describe('Entity Slice Enhancer', () => { + let slice: Slice> + + beforeEach(() => { + const indieSlice = entitySliceEnhancer({ + name: 'book', + selectId: (book: BookModel) => book.id, + }) + slice = indieSlice + }) + + it('exposes oneAdded', () => { + const book = { + id: '0', + title: 'Der Steppenwolf', + author: 'Herman Hesse', + } + const action = slice.actions.oneAdded(book) + const oneAdded = slice.reducer(undefined, action as UnknownAction) + expect(oneAdded.entities['0']).toBe(book) + }) +}) + +interface EntitySliceArgs { + name: string + selectId: IdSelector + modelReducer?: SliceCaseReducers +} + +function entitySliceEnhancer({ + name, + selectId, + modelReducer, +}: EntitySliceArgs) { + const modelAdapter = createEntityAdapter({ + selectId, + }) + + return createSlice({ + name, + initialState: modelAdapter.getInitialState(), + reducers: { + oneAdded(state, action: PayloadAction) { + modelAdapter.addOne(state, action.payload) + }, + ...modelReducer, + }, + }) +} diff --git a/website/README.md b/website/README.md index 7923efb9..71505291 100644 --- a/website/README.md +++ b/website/README.md @@ -1,33 +1,33 @@ -# Website - -This website is built using Docusaurus 2, a modern static website generator. - -### Installation - -``` -$ yarn -``` - -### Local Development - -``` -$ yarn start -``` - -This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server. - -### Build - -``` -$ yarn build -``` - -This command generates static content into the `build` directory and can be served using any static contents hosting service. - -### Deployment - -``` -$ GIT_USER= USE_SSH=1 yarn deploy -``` - -If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. +# Website + +This website is built using Docusaurus 2, a modern static website generator. + +### Installation + +``` +$ yarn +``` + +### Local Development + +``` +$ yarn start +``` + +This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server. + +### Build + +``` +$ yarn build +``` + +This command generates static content into the `build` directory and can be served using any static contents hosting service. + +### Deployment + +``` +$ GIT_USER= USE_SSH=1 yarn deploy +``` + +If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. diff --git a/website/sidebars.json b/website/sidebars.json index b3f2c944..ef79ee1a 100644 --- a/website/sidebars.json +++ b/website/sidebars.json @@ -1,150 +1,150 @@ -{ - "docs": [ - { - "type": "category", - "label": "Introduction", - "collapsed": false, - "items": [ - "introduction/getting-started", - "introduction/why-rtk-is-redux-today" - ] - }, - - { - "type": "category", - "label": "Tutorials", - "collapsed": false, - "items": [ - "tutorials/tutorials-overview", - "tutorials/quick-start", - "tutorials/typescript", - "tutorials/rtk-query" - ] - }, - { - "type": "category", - "label": "Using Redux Toolkit", - "collapsed": false, - "items": [ - { - "type": "category", - "label": "Migrations", - "items": ["usage/migrating-to-modern-redux", "usage/migrating-rtk-2"] - }, - "usage/usage-guide", - "usage/usage-with-typescript", - "usage/immer-reducers", - "usage/nextjs" - ] - }, - { - "type": "category", - "label": "API Reference", - "collapsed": true, - "items": [ - { - "type": "category", - "label": "Store Setup", - "collapsed": false, - "items": [ - "api/configureStore", - "api/getDefaultMiddleware", - "api/immutabilityMiddleware", - "api/serializabilityMiddleware", - "api/actionCreatorMiddleware", - "api/createListenerMiddleware", - "api/createDynamicMiddleware", - "api/getDefaultEnhancers", - "api/autoBatchEnhancer" - ] - }, - { - "type": "category", - "label": "Reducers and Actions", - "collapsed": false, - "items": [ - "api/createReducer", - "api/createAction", - "api/createSlice", - "api/createAsyncThunk", - "api/createEntityAdapter", - "api/combineSlices" - ] - }, - { - "type": "category", - "label": "Other", - "collapsed": false, - "items": [ - "api/createSelector", - "api/matching-utilities", - "api/other-exports", - "api/codemods", - { "type": "link", "label": "Error Messages", "href": "/errors" } - ] - } - ] - }, - { - "type": "category", - "label": "RTK Query", - "collapsed": false, - "items": [ - "rtk-query/overview", - "rtk-query/comparison", - "rtk-query/usage/examples", - "rtk-query/usage-with-typescript", - { - "type": "category", - "label": "Using RTK Query", - "collapsed": true, - "items": [ - "rtk-query/usage/queries", - "rtk-query/usage/mutations", - "rtk-query/usage/cache-behavior", - "rtk-query/usage/automated-refetching", - "rtk-query/usage/manual-cache-updates", - "rtk-query/usage/conditional-fetching", - "rtk-query/usage/error-handling", - "rtk-query/usage/pagination", - "rtk-query/usage/prefetching", - "rtk-query/usage/polling", - "rtk-query/usage/streaming-updates", - "rtk-query/usage/code-splitting", - "rtk-query/usage/code-generation", - "rtk-query/usage/server-side-rendering", - "rtk-query/usage/persistence-and-rehydration", - "rtk-query/usage/customizing-create-api", - "rtk-query/usage/customizing-queries", - "rtk-query/usage/usage-without-react-hooks", - "rtk-query/usage/migrating-to-rtk-query" - ] - }, - { - "type": "category", - "label": "API Reference", - "collapsed": true, - "items": [ - "rtk-query/api/createApi", - "rtk-query/api/fetchBaseQuery", - "rtk-query/api/ApiProvider", - "rtk-query/api/setupListeners", - { - "type": "category", - "label": "Generated API Slices", - "collapsed": false, - "items": [ - "rtk-query/api/created-api/overview", - "rtk-query/api/created-api/redux-integration", - "rtk-query/api/created-api/endpoints", - "rtk-query/api/created-api/code-splitting", - "rtk-query/api/created-api/api-slice-utils", - "rtk-query/api/created-api/hooks" - ] - } - ] - } - ] - } - ] -} +{ + "docs": [ + { + "type": "category", + "label": "Introduction", + "collapsed": false, + "items": [ + "introduction/getting-started", + "introduction/why-rtk-is-redux-today" + ] + }, + + { + "type": "category", + "label": "Tutorials", + "collapsed": false, + "items": [ + "tutorials/tutorials-overview", + "tutorials/quick-start", + "tutorials/typescript", + "tutorials/rtk-query" + ] + }, + { + "type": "category", + "label": "Using Redux Toolkit", + "collapsed": false, + "items": [ + { + "type": "category", + "label": "Migrations", + "items": ["usage/migrating-to-modern-redux", "usage/migrating-rtk-2"] + }, + "usage/usage-guide", + "usage/usage-with-typescript", + "usage/immer-reducers", + "usage/nextjs" + ] + }, + { + "type": "category", + "label": "API Reference", + "collapsed": true, + "items": [ + { + "type": "category", + "label": "Store Setup", + "collapsed": false, + "items": [ + "api/configureStore", + "api/getDefaultMiddleware", + "api/immutabilityMiddleware", + "api/serializabilityMiddleware", + "api/actionCreatorMiddleware", + "api/createListenerMiddleware", + "api/createDynamicMiddleware", + "api/getDefaultEnhancers", + "api/autoBatchEnhancer" + ] + }, + { + "type": "category", + "label": "Reducers and Actions", + "collapsed": false, + "items": [ + "api/createReducer", + "api/createAction", + "api/createSlice", + "api/createAsyncThunk", + "api/createEntityAdapter", + "api/combineSlices" + ] + }, + { + "type": "category", + "label": "Other", + "collapsed": false, + "items": [ + "api/createSelector", + "api/matching-utilities", + "api/other-exports", + "api/codemods", + { "type": "link", "label": "Error Messages", "href": "/errors" } + ] + } + ] + }, + { + "type": "category", + "label": "RTK Query", + "collapsed": false, + "items": [ + "rtk-query/overview", + "rtk-query/comparison", + "rtk-query/usage/examples", + "rtk-query/usage-with-typescript", + { + "type": "category", + "label": "Using RTK Query", + "collapsed": true, + "items": [ + "rtk-query/usage/queries", + "rtk-query/usage/mutations", + "rtk-query/usage/cache-behavior", + "rtk-query/usage/automated-refetching", + "rtk-query/usage/manual-cache-updates", + "rtk-query/usage/conditional-fetching", + "rtk-query/usage/error-handling", + "rtk-query/usage/pagination", + "rtk-query/usage/prefetching", + "rtk-query/usage/polling", + "rtk-query/usage/streaming-updates", + "rtk-query/usage/code-splitting", + "rtk-query/usage/code-generation", + "rtk-query/usage/server-side-rendering", + "rtk-query/usage/persistence-and-rehydration", + "rtk-query/usage/customizing-create-api", + "rtk-query/usage/customizing-queries", + "rtk-query/usage/usage-without-react-hooks", + "rtk-query/usage/migrating-to-rtk-query" + ] + }, + { + "type": "category", + "label": "API Reference", + "collapsed": true, + "items": [ + "rtk-query/api/createApi", + "rtk-query/api/fetchBaseQuery", + "rtk-query/api/ApiProvider", + "rtk-query/api/setupListeners", + { + "type": "category", + "label": "Generated API Slices", + "collapsed": false, + "items": [ + "rtk-query/api/created-api/overview", + "rtk-query/api/created-api/redux-integration", + "rtk-query/api/created-api/endpoints", + "rtk-query/api/created-api/code-splitting", + "rtk-query/api/created-api/api-slice-utils", + "rtk-query/api/created-api/hooks" + ] + } + ] + } + ] + } + ] +} diff --git a/website/src/js/monokaiTheme.js b/website/src/js/monokaiTheme.js index 94e5cb45..bd3eebd1 100644 --- a/website/src/js/monokaiTheme.js +++ b/website/src/js/monokaiTheme.js @@ -1,62 +1,62 @@ -module.exports = { - plain: { - color: '#f8f8f2', - backgroundColor: '#272822', - }, - styles: [ - { - types: ['comment', 'prolog', 'doctype', 'cdata'], - style: { - color: '#778090', - }, - }, - { - types: ['punctuation'], - style: { - color: '#F8F8F2', - }, - }, - { - types: ['property', 'tag', 'constant', 'symbol', 'deleted'], - style: { - color: '#F92672', - }, - }, - { - types: ['boolean', 'number'], - style: { - color: '#AE81FF', - }, - }, - { - types: ['selector', 'attr-name', 'string', 'char', 'builtin', 'inserted'], - style: { - color: '#a6e22e', - }, - }, - { - types: ['operator', 'entity', 'url', 'variable'], - style: { - color: '#F8F8F2', - }, - }, - { - types: ['atrule', 'attr-value', 'function'], - style: { - color: '#E6D874', - }, - }, - { - types: ['keyword'], - style: { - color: '#F92672', - }, - }, - { - types: ['regex', 'important'], - style: { - color: '#FD971F', - }, - }, - ], -} +module.exports = { + plain: { + color: '#f8f8f2', + backgroundColor: '#272822', + }, + styles: [ + { + types: ['comment', 'prolog', 'doctype', 'cdata'], + style: { + color: '#778090', + }, + }, + { + types: ['punctuation'], + style: { + color: '#F8F8F2', + }, + }, + { + types: ['property', 'tag', 'constant', 'symbol', 'deleted'], + style: { + color: '#F92672', + }, + }, + { + types: ['boolean', 'number'], + style: { + color: '#AE81FF', + }, + }, + { + types: ['selector', 'attr-name', 'string', 'char', 'builtin', 'inserted'], + style: { + color: '#a6e22e', + }, + }, + { + types: ['operator', 'entity', 'url', 'variable'], + style: { + color: '#F8F8F2', + }, + }, + { + types: ['atrule', 'attr-value', 'function'], + style: { + color: '#E6D874', + }, + }, + { + types: ['keyword'], + style: { + color: '#F92672', + }, + }, + { + types: ['regex', 'important'], + style: { + color: '#FD971F', + }, + }, + ], +} diff --git a/website/src/pages/index.js b/website/src/pages/index.js index 82f02a91..f7b43f53 100644 --- a/website/src/pages/index.js +++ b/website/src/pages/index.js @@ -1,213 +1,213 @@ -import React from 'react' -import classnames from 'classnames' -import Layout from '@theme/Layout' -import Link from '@docusaurus/Link' -import useDocusaurusContext from '@docusaurus/useDocusaurusContext' -import useBaseUrl from '@docusaurus/useBaseUrl' -import styles from './styles.module.css' - -const features = [ - { - content: ( -

    - Includes utilities to simplify common use cases like{' '} - store setup, creating reducers, immutable update logic, - and more. -

    - ), - image: ( - - - - - - - ), - imageAlign: 'top', - title: 'Simple', - }, - { - content: ( -

    - Provides good defaults for store setup out of the box, - and includes{' '} - the most commonly used Redux addons built-in. -

    - ), - image: ( - - - - - ), - imageAlign: 'top', - title: 'Opinionated', - }, - { - content: ( -

    - Takes inspiration from libraries like Immer and Autodux to let you{' '} - write "mutative" immutable update logic, and even{' '} - create entire "slices" of state automatically. -

    - ), - image: ( - - - - ), - imageAlign: 'top', - title: 'Powerful', - }, - { - content: ( -

    - Lets you focus on the core logic your app needs, so you can{' '} - do more work with less code. -

    - ), - image: ( - - - - ), - imageAlign: 'top', - title: 'Effective', - }, -] - -const otherLibraries = [ - { - content: 'A predictable state container for JavaScript applications', - title: 'Redux', - link: 'https://redux.js.org', - image: ( - - ), - }, - { - content: 'Official React bindings for Redux', - title: 'React-Redux', - link: 'https://react-redux.js.org', - image: ( - - ), - }, -] - -function Home() { - const context = useDocusaurusContext() - const { siteConfig = {} } = context - - return ( - -
    -
    -
    - Redux logo -

    - {siteConfig.title} -

    -
    -

    {siteConfig.tagline}

    -
    - - Get Started - -
    -
    -
    -
    - {features && features.length && ( -
    -
    -
    - {features.map(({ image, title, content }, idx) => ( -
    - {image && ( -
    - {image} -
    - )} -

    - {title} -

    -
    {content}
    -
    - ))} -
    -
    -
    - )} - {otherLibraries && otherLibraries.length && ( -
    -
    -
    -
    -

    - Other Libraries from the Redux Team -

    -
    -
    -
    - {otherLibraries.map(({ image, title, content, link }, idx) => ( -
    -

    - - {title} - {image} - -

    -

    {content}

    -
    - ))} -
    -
    -
    - )} -
    -
    - ) -} - -export default Home +import React from 'react' +import classnames from 'classnames' +import Layout from '@theme/Layout' +import Link from '@docusaurus/Link' +import useDocusaurusContext from '@docusaurus/useDocusaurusContext' +import useBaseUrl from '@docusaurus/useBaseUrl' +import styles from './styles.module.css' + +const features = [ + { + content: ( +

    + Includes utilities to simplify common use cases like{' '} + store setup, creating reducers, immutable update logic, + and more. +

    + ), + image: ( + + + + + + + ), + imageAlign: 'top', + title: 'Simple', + }, + { + content: ( +

    + Provides good defaults for store setup out of the box, + and includes{' '} + the most commonly used Redux addons built-in. +

    + ), + image: ( + + + + + ), + imageAlign: 'top', + title: 'Opinionated', + }, + { + content: ( +

    + Takes inspiration from libraries like Immer and Autodux to let you{' '} + write "mutative" immutable update logic, and even{' '} + create entire "slices" of state automatically. +

    + ), + image: ( + + + + ), + imageAlign: 'top', + title: 'Powerful', + }, + { + content: ( +

    + Lets you focus on the core logic your app needs, so you can{' '} + do more work with less code. +

    + ), + image: ( + + + + ), + imageAlign: 'top', + title: 'Effective', + }, +] + +const otherLibraries = [ + { + content: 'A predictable state container for JavaScript applications', + title: 'Redux', + link: 'https://redux.js.org', + image: ( + + ), + }, + { + content: 'Official React bindings for Redux', + title: 'React-Redux', + link: 'https://react-redux.js.org', + image: ( + + ), + }, +] + +function Home() { + const context = useDocusaurusContext() + const { siteConfig = {} } = context + + return ( + +
    +
    +
    + Redux logo +

    + {siteConfig.title} +

    +
    +

    {siteConfig.tagline}

    +
    + + Get Started + +
    +
    +
    +
    + {features && features.length && ( +
    +
    +
    + {features.map(({ image, title, content }, idx) => ( +
    + {image && ( +
    + {image} +
    + )} +

    + {title} +

    +
    {content}
    +
    + ))} +
    +
    +
    + )} + {otherLibraries && otherLibraries.length && ( +
    +
    +
    +
    +

    + Other Libraries from the Redux Team +

    +
    +
    +
    + {otherLibraries.map(({ image, title, content, link }, idx) => ( +
    +

    + + {title} + {image} + +

    +

    {content}

    +
    + ))} +
    +
    +
    + )} +
    +
    + ) +} + +export default Home diff --git a/website/src/pages/styles.module.css b/website/src/pages/styles.module.css index 54f97730..f9b824ce 100644 --- a/website/src/pages/styles.module.css +++ b/website/src/pages/styles.module.css @@ -1,110 +1,110 @@ -/** - * CSS files with the .module.css suffix will be treated as CSS modules - * and scoped locally. - */ - -.heroBanner { - padding: 4rem 0; - text-align: center; - position: relative; - overflow: hidden; -} - -@media screen and (max-width: 966px) { - .heroBanner { - padding: 2rem; - } -} - -.buttons { - display: flex; - align-items: center; - justify-content: center; -} - -.features { - display: flex; - align-items: center; - padding: 2rem 0; - width: 100%; -} - -.featureBlock { - margin-top: 40px; -} - -.blockImage { - margin-bottom: 20px; -} - -.blockImage svg { - fill: currentColor; - width: 60px; - height: 60px; -} - -.featureTitle { - font-size: 30px; - padding: 10px 0; -} - -@media screen and (max-width: 966px) { - .featureContent { - text-align: center; - } -} - -.title { - display: flex; - justify-content: center; - align-items: center; -} - -.projectTitle { - margin: 0.3em 0; -} - -.featureAnchor svg { - margin-left: 5px; - max-width: 16px; - max-height: 16px; - fill: currentColor; -} - -.getStarted:hover { - background: #fff; - color: var(--ifm-color-primary); -} - -.secondTitle { - font-size: 30px; - margin-bottom: 55px; -} - -.mainFull { - padding: 34px 16px; - width: 100%; - max-width: var(--ifm-container-width); - margin: 0px auto; -} -.mainFull h1 { - font-size: 3rem; - color: var(--ifm-heading-color); - font-weight: var(--ifm-heading-font-weight); - line-height: var(--ifm-heading-line-height); -} - -.mainFull p { - margin-bottom: var(--ifm-leading); - margin-top: 0; -} - -.errorDetails { - color: #ff6464; - border-radius: 0.5rem; - padding: 1rem; - margin: 30px 0; - font-weight: bold; - background-color: rgba(255, 100, 100, 0.1); - display: block; -} +/** + * CSS files with the .module.css suffix will be treated as CSS modules + * and scoped locally. + */ + +.heroBanner { + padding: 4rem 0; + text-align: center; + position: relative; + overflow: hidden; +} + +@media screen and (max-width: 966px) { + .heroBanner { + padding: 2rem; + } +} + +.buttons { + display: flex; + align-items: center; + justify-content: center; +} + +.features { + display: flex; + align-items: center; + padding: 2rem 0; + width: 100%; +} + +.featureBlock { + margin-top: 40px; +} + +.blockImage { + margin-bottom: 20px; +} + +.blockImage svg { + fill: currentColor; + width: 60px; + height: 60px; +} + +.featureTitle { + font-size: 30px; + padding: 10px 0; +} + +@media screen and (max-width: 966px) { + .featureContent { + text-align: center; + } +} + +.title { + display: flex; + justify-content: center; + align-items: center; +} + +.projectTitle { + margin: 0.3em 0; +} + +.featureAnchor svg { + margin-left: 5px; + max-width: 16px; + max-height: 16px; + fill: currentColor; +} + +.getStarted:hover { + background: #fff; + color: var(--ifm-color-primary); +} + +.secondTitle { + font-size: 30px; + margin-bottom: 55px; +} + +.mainFull { + padding: 34px 16px; + width: 100%; + max-width: var(--ifm-container-width); + margin: 0px auto; +} +.mainFull h1 { + font-size: 3rem; + color: var(--ifm-heading-color); + font-weight: var(--ifm-heading-font-weight); + line-height: var(--ifm-heading-line-height); +} + +.mainFull p { + margin-bottom: var(--ifm-leading); + margin-top: 0; +} + +.errorDetails { + color: #ff6464; + border-radius: 0.5rem; + padding: 1rem; + margin: 30px 0; + font-weight: bold; + background-color: rgba(255, 100, 100, 0.1); + display: block; +} -- 2.51.2