TypeScript での使用方法
- TypeScript で様々な RTK Query API を使用する方法の詳細
はじめに
Redux Toolkit パッケージの他の部分と同様に、RTK Query は TypeScript で記述されており、その API は TypeScript アプリケーションでシームレスに使用できるように設計されています。
このページでは、RTK Query に含まれる API を TypeScript で使用する方法と、それらを正しく型付けする方法について詳しく説明します。
最良の結果を得るには、RTK Query で TypeScript 4.1 以降を使用することを強くお勧めします。
このページに記載されていない型の問題が発生した場合は、issue を開いてください。
createApi
自動生成された React Hooks の使用
RTK Query の React 固有のエントリポイントは、定義されたクエリとミューテーションのそれぞれに React hooks を自動的に生成する createApi
のバージョンをエクスポートします endpoints
。
TypeScript ユーザーとして自動生成された React Hooks を使用するには、**TS4.1 以降を使用する必要があります**。
- TypeScript
- JavaScript
// Need to use the React-specific entry point to allow generating React hooks
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Pokemon } from './types'
// Define a service using a base URL and expected endpoints
export const pokemonApi = createApi({
reducerPath: 'pokemonApi',
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
endpoints: (builder) => ({
getPokemonByName: builder.query<Pokemon, string>({
query: (name) => `pokemon/${name}`,
}),
}),
})
// Export hooks for usage in function components, which are
// auto-generated based on the defined endpoints
export const { useGetPokemonByNameQuery } = pokemonApi
// Need to use the React-specific entry point to allow generating React hooks
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
// Define a service using a base URL and expected endpoints
export const pokemonApi = createApi({
reducerPath: 'pokemonApi',
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
endpoints: (builder) => ({
getPokemonByName: builder.query({
query: (name) => `pokemon/${name}`,
}),
}),
})
// Export hooks for usage in function components, which are
// auto-generated based on the defined endpoints
export const { useGetPokemonByNameQuery } = pokemonApi
古いバージョンの TS の場合は、api.endpoints.[endpointName].useQuery/useMutation
を使用して同じ hooks にアクセスできます。
- TypeScript
- JavaScript
import { pokemonApi } from './pokemon'
const useGetPokemonByNameQuery = pokemonApi.endpoints.getPokemonByName.useQuery
import { pokemonApi } from './pokemon'
const useGetPokemonByNameQuery = pokemonApi.endpoints.getPokemonByName.useQuery
baseQuery
の型付け
カスタム baseQuery
の型付けは、RTK Query によってエクスポートされた BaseQueryFn
型を使用して行うことができます。
export type BaseQueryFn<
Args = any,
Result = unknown,
Error = unknown,
DefinitionExtraOptions = {},
Meta = {},
> = (
args: Args,
api: BaseQueryApi,
extraOptions: DefinitionExtraOptions,
) => MaybePromise<QueryReturnValue<Result, Error, Meta>>
export interface BaseQueryApi {
signal: AbortSignal
dispatch: ThunkDispatch<any, any, any>
getState: () => unknown
}
export type QueryReturnValue<T = unknown, E = unknown, M = unknown> =
| {
error: E
data?: undefined
meta?: M
}
| {
error?: undefined
data: T
meta?: M
}
BaseQueryFn
型は、次のジェネリクスを受け入れます
Args
- 関数の最初のパラメーターの型。エンドポイントのquery
プロパティによって返された結果がここに渡されます。Result
- 成功した場合にdata
プロパティで返される型。すべてのクエリとミューテーションが同じ型を返すことが予想されない限り、これをunknown
として型付けし、以下に示すように個別に型を指定することをお勧めします。Error
- エラーの場合にerror
プロパティに返される型。この型は、API 定義全体でエンドポイントで使用されるすべてのqueryFn
関数にも適用されます。DefinitionExtraOptions
- 関数の3番目のパラメーターの型。エンドポイントのextraOptions
プロパティに提供された値がここに渡されます。Meta
-baseQuery
の呼び出しから返される可能性のあるmeta
プロパティの型。meta
プロパティは、transformResponse
およびtransformErrorResponse
の2番目の引数としてアクセスできます。
baseQuery
から返された meta
プロパティは、エラーケースで throw
が発生すると提供されない場合があるため、常に未定義の可能性があると見なされます。meta
プロパティから値にアクセスする場合は、オプションチェーンなどを使用して、これを考慮に入れる必要があります。
- TypeScript
- JavaScript
import { createApi } from '@reduxjs/toolkit/query'
import type { BaseQueryFn } from '@reduxjs/toolkit/query'
const simpleBaseQuery: BaseQueryFn<
string, // Args
unknown, // Result
{ reason: string }, // Error
{ shout?: boolean }, // DefinitionExtraOptions
{ timestamp: number } // Meta
> = (arg, api, extraOptions) => {
// `arg` has the type `string`
// `api` has the type `BaseQueryApi` (not configurable)
// `extraOptions` has the type `{ shout?: boolean }
const meta = { timestamp: Date.now() }
if (arg === 'forceFail') {
return {
error: {
reason: 'Intentionally requested to fail!',
meta,
},
}
}
if (extraOptions.shout) {
return { data: 'CONGRATULATIONS', meta }
}
return { data: 'congratulations', meta }
}
const api = createApi({
baseQuery: simpleBaseQuery,
endpoints: (builder) => ({
getSupport: builder.query({
query: () => 'support me',
extraOptions: {
shout: true,
},
}),
}),
})
import { createApi } from '@reduxjs/toolkit/query'
const simpleBaseQuery = (arg, api, extraOptions) => {
// `arg` has the type `string`
// `api` has the type `BaseQueryApi` (not configurable)
// `extraOptions` has the type `{ shout?: boolean }
const meta = { timestamp: Date.now() }
if (arg === 'forceFail') {
return {
error: {
reason: 'Intentionally requested to fail!',
meta,
},
}
}
if (extraOptions.shout) {
return { data: 'CONGRATULATIONS', meta }
}
return { data: 'congratulations', meta }
}
const api = createApi({
baseQuery: simpleBaseQuery,
endpoints: (builder) => ({
getSupport: builder.query({
query: () => 'support me',
extraOptions: {
shout: true,
},
}),
}),
})
クエリとミューテーション endpoints
の型付け
API の endpoints
は、ビルダー構文を使用してオブジェクトとして定義されます。query
と mutation
の両方のエンドポイントは、<ResultType, QueryArg>
形式でジェネリクスに型を提供することで型付けできます。
ResultType
- オプションのtransformResponse
を考慮した、クエリによって返される最終データの型。transformResponse
が提供されない場合、成功したクエリはこの型を返すかのように扱われます。transformResponse
が提供される場合、初期クエリが返す型を示すために、transformResponse
の入力型も指定する必要があります。transformResponse
の戻り値の型はResultType
と一致する必要があります。query
ではなくqueryFn
が使用される場合、成功した場合に次の形状を返す必要があります{
data: ResultType
}
QueryArg
- エンドポイントのquery
プロパティの唯一のパラメーターとして、または代わりにqueryFn
プロパティが使用される場合は最初のパラメーターとして渡される入力の型。query
にパラメーターがない場合は、void
型を明示的に指定する必要があります。query
にオプションのパラメーターがある場合は、パラメーターの型とvoid
を持つユニオン型を指定する必要があります。例:number | void
。
- TypeScript
- JavaScript
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
interface Post {
id: number
name: string
}
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
// ResultType QueryArg
// v v
getPost: build.query<Post, number>({
// inferred as `number` from the `QueryArg` type
// v
query: (id) => `post/${id}`,
// An explicit type must be provided to the raw result that the query returns
// when using `transformResponse`
// v
transformResponse: (rawResult: { result: { post: Post } }, meta) => {
// ^
// The optional `meta` property is available based on the type for the `baseQuery` used
// The return value for `transformResponse` must match `ResultType`
return rawResult.result.post
},
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
// ResultType QueryArg
// v v
getPost: build.query({
// inferred as `number` from the `QueryArg` type
// v
query: (id) => `post/${id}`,
// An explicit type must be provided to the raw result that the query returns
// when using `transformResponse`
// v
transformResponse: (rawResult, meta) => {
// ^
// The optional `meta` property is available based on the type for the `baseQuery` used
// The return value for `transformResponse` must match `ResultType`
return rawResult.result.post
},
}),
}),
})
queries
と mutations
は、上記の方法ではなく baseQuery
によって戻り値の型を定義することもできますが、すべてのクエリとミューテーションが同じ型を返すことが予想されない限り、baseQuery
の戻り値の型を unknown
のままにすることをお勧めします。
queryFn
の型付け
クエリとミューテーションエンドポイントの型付け で述べたように、queryFn
は対応するビルドエンドポイントに提供されたジェネリクスから結果と引数の型を受け取ります。
- TypeScript
- JavaScript
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { getRandomName } from './randomData'
interface Post {
id: number
name: string
}
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
// ResultType QueryArg
// v v
getPost: build.query<Post, number>({
// inferred as `number` from the `QueryArg` type
// v
queryFn: (arg, queryApi, extraOptions, baseQuery) => {
const post: Post = {
id: arg,
name: getRandomName(),
}
// For the success case, the return type for the `data` property
// must match `ResultType`
// v
return { data: post }
},
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { getRandomName } from './randomData'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
// ResultType QueryArg
// v v
getPost: build.query({
// inferred as `number` from the `QueryArg` type
// v
queryFn: (arg, queryApi, extraOptions, baseQuery) => {
const post = {
id: arg,
name: getRandomName(),
}
// For the success case, the return type for the `data` property
// must match `ResultType`
// v
return { data: post }
},
}),
}),
})
queryFn
が返す必要があるエラー型は、createApi
に提供された baseQuery
によって決まります。
fetchBaseQuery
を使用すると、エラー型は次のようになります
{
status: number
data: any
}
queryFn
と fetchBaseQuery
のエラー型を使用した上記の例のエラーケースは、次のようになります
- TypeScript
- JavaScript
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { getRandomName } from './randomData'
interface Post {
id: number
name: string
}
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
getPost: build.query<Post, number>({
queryFn: (arg, queryApi, extraOptions, baseQuery) => {
if (arg <= 0) {
return {
error: {
status: 500,
statusText: 'Internal Server Error',
data: 'Invalid ID provided.',
},
}
}
const post: Post = {
id: arg,
name: getRandomName(),
}
return { data: post }
},
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { getRandomName } from './randomData'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
getPost: build.query({
queryFn: (arg, queryApi, extraOptions, baseQuery) => {
if (arg <= 0) {
return {
error: {
status: 500,
statusText: 'Internal Server Error',
data: 'Invalid ID provided.',
},
}
}
const post = {
id: arg,
name: getRandomName(),
}
return { data: post }
},
}),
}),
})
各エンドポイントに queryFn
のみを使用し、baseQuery
をまったく含まないユーザーのために、RTK Query は各 queryFn
が返す必要があるエラー型を簡単に指定できる fakeBaseQuery
関数を提供します。
- TypeScript
- JavaScript
import { createApi, fakeBaseQuery } from '@reduxjs/toolkit/query'
type CustomErrorType = { reason: 'too cold' | 'too hot' }
const api = createApi({
// This type will be used as the error type for all `queryFn` functions provided
// v
baseQuery: fakeBaseQuery<CustomErrorType>(),
endpoints: (build) => ({
eatPorridge: build.query<'just right', 1 | 2 | 3>({
queryFn(seat) {
if (seat === 1) {
return { error: { reason: 'too cold' } }
}
if (seat === 2) {
return { error: { reason: 'too hot' } }
}
return { data: 'just right' }
},
}),
microwaveHotPocket: build.query<'delicious!', number>({
queryFn(duration) {
if (duration < 110) {
return { error: { reason: 'too cold' } }
}
if (duration > 140) {
return { error: { reason: 'too hot' } }
}
return { data: 'delicious!' }
},
}),
}),
})
import { createApi, fakeBaseQuery } from '@reduxjs/toolkit/query'
const api = createApi({
// This type will be used as the error type for all `queryFn` functions provided
// v
baseQuery: fakeBaseQuery(),
endpoints: (build) => ({
eatPorridge: build.query({
queryFn(seat) {
if (seat === 1) {
return { error: { reason: 'too cold' } }
}
if (seat === 2) {
return { error: { reason: 'too hot' } }
}
return { data: 'just right' }
},
}),
microwaveHotPocket: build.query({
queryFn(duration) {
if (duration < 110) {
return { error: { reason: 'too cold' } }
}
if (duration > 140) {
return { error: { reason: 'too hot' } }
}
return { data: 'delicious!' }
},
}),
}),
})
dispatch
と getState
の型付け
createApi
は、ライフサイクルメソッドの lifecycleApi
引数や、queryFn
メソッドと base query 関数に渡される baseQueryApi
引数など、いくつかの場所で標準の Redux dispatch
と getState
メソッドを公開します。
通常、アプリケーションはストアのセットアップから RootState
と AppDispatch
の型を推測します。createApi
は Redux ストアを作成する前に呼び出す必要があり、ストアのセットアップシーケンスの一部として使用されるため、これらの型を直接認識したり使用したりすることはできません。循環型の推論エラーが発生します。
デフォルトでは、createApi
内の dispatch
の使用は ThunkDispatch
として型付けされ、getState
の使用は () => unknown
として型付けされます。必要に応じて型をアサートする必要があります - getState() as RootState
。循環型の推論サイクルを断ち切るために、関数に明示的な戻り値の型を含めることもできます
const api = createApi({
baseQuery,
endpoints: (build) => ({
getTodos: build.query<Todo[], void>({
async queryFn() {
// Cast state as `RootState`
const state = getState() as RootState
const text = state.todoTexts[queryFnCalls]
return { data: [{ id: `${queryFnCalls++}`, text }] }
},
}),
}),
})
providesTags
/invalidatesTags
の型付け
RTK Query は、古いデータの自動再フェッチ を提供するために、キャッシュタグ無効化システムを利用します。
関数表記を使用する場合、エンドポイントの providesTags
プロパティと invalidatesTags
プロパティの両方が、次の引数で呼び出されます
- result:
ResultType
|undefined
- 成功したクエリによって返された結果。型は、ビルドエンドポイントに提供されたResultType
に対応します。クエリのエラーケースでは、これはundefined
になります。 - error:
ErrorType
|undefined
- エラーが発生したクエリによって返されたエラー。型は、API のbaseQuery
に提供されたError
に対応します。クエリの成功ケースでは、これはundefined
になります。 - arg:
QueryArg
- クエリ自体が呼び出されたときにquery
プロパティに提供された引数。型は、ビルドエンドポイントに提供されたQueryArg
に対応します。
クエリが項目のリストを返す場合の providesTags
の推奨されるユースケースは、エンティティ ID を使用してリストの各項目にタグを提供し、'LIST' ID タグを提供することです(抽象タグ ID を使用した高度な無効化 を参照)。
これは多くの場合、受信したデータを配列にマッピングした結果と、'LIST'
ID タグの配列に追加の項目をスプレッドすることで記述されます。マッピングされた配列をスプレッドすると、デフォルトでは TypeScript は type
プロパティを string
に広げます。タグ type
は API の tagTypes
プロパティに提供された文字列リテラルのいずれかに対応する必要があるため、広範な string
型では TypeScript を満たすことができません。これを軽減するために、タグ type
を as const
にキャストして、型が string
に広げられないようにすることができます。
- TypeScript
- JavaScript
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
interface Post {
id: number
name: string
}
type PostsResponse = Post[]
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Posts'],
endpoints: (build) => ({
getPosts: build.query<PostsResponse, void>({
query: () => 'posts',
providesTags: (result) =>
result
? [
...result.map(({ id }) => ({ type: 'Posts' as const, id })),
{ type: 'Posts', id: 'LIST' },
]
: [{ type: 'Posts', id: 'LIST' }],
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Posts'],
endpoints: (build) => ({
getPosts: build.query({
query: () => 'posts',
providesTags: (result) =>
result
? [
...result.map(({ id }) => ({ type: 'Posts', id })),
{ type: 'Posts', id: 'LIST' },
]
: [{ type: 'Posts', id: 'LIST' }],
}),
}),
})
skipToken
を使用して TypeScript でクエリをスキップする
RTK Query は、クエリフックオプションの一部として skip
パラメーターを使用して、クエリが自動的に実行されないように条件付きでスキップする機能を提供します(条件付きフェッチ を参照)。
TypeScriptユーザーは、クエリ引数の型がundefined
ではないと定義されている場合に、引数が無効になる可能性があるクエリをskip
しようとすると、無効な型シナリオに遭遇する可能性があります。
- TypeScript
- JavaScript
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Post } from './types'
export const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
// Query argument is required to be `number`, and can't be `undefined`
// V
getPost: build.query<Post, number>({
query: (id) => `post/${id}`,
}),
}),
})
export const { useGetPostQuery } = api
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
export const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
// Query argument is required to be `number`, and can't be `undefined`
// V
getPost: build.query({
query: (id) => `post/${id}`,
}),
}),
})
export const { useGetPostQuery } = api
import { useGetPostQuery } from './api'
function MaybePost({ id }: { id?: number }) {
// This will produce a typescript error:
// Argument of type 'number | undefined' is not assignable to parameter of type 'number | unique symbol'.
// Type 'undefined' is not assignable to type 'number | unique symbol'.
// @ts-expect-error id passed must be a number, but we don't call it when it isn't a number
const { data } = useGetPostQuery(id, { skip: !id })
return <div>...</div>
}
id
引数がnumber
型である場合を除いて、クエリは呼び出されないことを確信できるかもしれませんが、TypeScriptはそれほど簡単に納得しません。
RTK Queryは、クエリのスキップにskip
オプションの代わりに使用できるskipToken
エクスポートを提供し、型の安全性を維持します。 skipToken
がuseQuery
、useQueryState
、またはuseQuerySubscription
へのクエリ引数として渡されると、クエリオプションでskip: true
を設定するのと同じ効果が得られます。また、arg
が未定義になる可能性のあるシナリオでも有効な引数となります。
import { skipToken } from '@reduxjs/toolkit/query/react'
import { useGetPostQuery } from './api'
function MaybePost({ id }: { id?: number }) {
// When `id` is nullish, we will still skip the query.
// TypeScript is also happy that the query will only ever be called with a `number` now
const { data } = useGetPostQuery(id ?? skipToken)
return <div>...</div>
}
タイプセーフなエラー処理
base query
からエラーが正常に提供された場合、RTKクエリはエラーを直接提供します。処理されたエラーではなく、ユーザーコードによって予期しないエラーがスローされた場合、そのエラーはSerializedError
の形に変換されます。ユーザーは、プロパティにアクセスする前に、どの種類のエラーを処理しているかを確認する必要があります。これは、判別プロパティをチェックするなど、タイプガードを使用するか、タイプ述語を使用して、タイプセーフな方法で行うことができます。
fetchBaseQuery
をベースクエリとして使用する場合、エラーの型はFetchBaseQueryError | SerializedError
になります。これらの型の具体的な形状は以下を参照してください。
- TypeScript
- JavaScript
export type FetchBaseQueryError =
| {
/**
* * `number`:
* HTTP status code
*/
status: number
data: unknown
}
| {
/**
* * `"FETCH_ERROR"`:
* An error that occurred during execution of `fetch` or the `fetchFn` callback option
**/
status: 'FETCH_ERROR'
data?: undefined
error: string
}
| {
/**
* * `"PARSING_ERROR"`:
* An error happened during parsing.
* Most likely a non-JSON-response was returned with the default `responseHandler` "JSON",
* or an error occurred while executing a custom `responseHandler`.
**/
status: 'PARSING_ERROR'
originalStatus: number
data: string
error: string
}
| {
/**
* * `"CUSTOM_ERROR"`:
* A custom error type that you can return from your `queryFn` where another error might not make sense.
**/
status: 'CUSTOM_ERROR'
data?: unknown
error: string
}
export {}
- TypeScript
- JavaScript
export interface SerializedError {
name?: string
message?: string
stack?: string
code?: string
}
export {}
エラー結果の例
fetchBaseQuery
を使用する場合、フックから返されるerror
プロパティの型はFetchBaseQueryError | SerializedError | undefined
になります。エラーが存在する場合、型をFetchBaseQueryError
またはSerializedError
に絞り込んだ後にエラープロパティにアクセスできます。
import { usePostsQuery } from './services/api'
function PostDetail() {
const { data, error, isLoading } = usePostsQuery()
if (isLoading) {
return <div>Loading...</div>
}
if (error) {
if ('status' in error) {
// you can access all properties of `FetchBaseQueryError` here
const errMsg = 'error' in error ? error.error : JSON.stringify(error.data)
return (
<div>
<div>An error has occurred:</div>
<div>{errMsg}</div>
</div>
)
}
// you can access all properties of `SerializedError` here
return <div>{error.message}</div>
}
if (data) {
return (
<div>
{data.map((post) => (
<div key={post.id}>Name: {post.name}</div>
))}
</div>
)
}
return null
}
インラインエラー処理の例
ミューテーション呼び出しをアンラップ
した後にインラインでエラーを処理する場合、スローされたエラーの型は、TypeScriptバージョン4.4未満ではany
、バージョン4.4以上ではunknown
になります。エラーのプロパティに安全にアクセスするには、最初に型を既知の型に絞り込む必要があります。これは、以下に示すように、タイプ述語を使用して行うことができます。
import { FetchBaseQueryError } from '@reduxjs/toolkit/query'
/**
* Type predicate to narrow an unknown error to `FetchBaseQueryError`
*/
export function isFetchBaseQueryError(
error: unknown,
): error is FetchBaseQueryError {
return typeof error === 'object' && error != null && 'status' in error
}
/**
* Type predicate to narrow an unknown error to an object with a string 'message' property
*/
export function isErrorWithMessage(
error: unknown,
): error is { message: string } {
return (
typeof error === 'object' &&
error != null &&
'message' in error &&
typeof (error as any).message === 'string'
)
}
import { useState } from 'react'
import { useSnackbar } from 'notistack'
import { api } from './services/api'
import { isFetchBaseQueryError, isErrorWithMessage } from './services/helpers'
function AddPost() {
const { enqueueSnackbar, closeSnackbar } = useSnackbar()
const [name, setName] = useState('')
const [addPost] = useAddPostMutation()
async function handleAddPost() {
try {
await addPost(name).unwrap()
setName('')
} catch (err) {
if (isFetchBaseQueryError(err)) {
// you can access all properties of `FetchBaseQueryError` here
const errMsg = 'error' in err ? err.error : JSON.stringify(err.data)
enqueueSnackbar(errMsg, { variant: 'error' })
} else if (isErrorWithMessage(err)) {
// you can access a string 'message' property here
enqueueSnackbar(err.message, { variant: 'error' })
}
}
}
return (
<div>
<input value={name} onChange={(e) => setName(e.target.value)} />
<button>Add post</button>
</div>
)
}