内容
React Queryの学習用メモとしてまとめる。
※パフォーマンスは未考慮。
APIはjsonplaceholderを使用して、実装を確認。
開発環境
PC:macOS Monterey(Intel)
エディター:Visual Studio Code
パッケージマネージャー: yarn
パッケージ:
react: 18.1.0
react-query: 3.39.1
typescript: 4.6.4
API
useQuery
useQueryは一意のqueryKeyに基づいて、クエリを管理することができる。
useQuery.d.tsには以下の3つのuseQuery関数が記載されている。(一部省略)
useQuery.d.ts
useQuery(options);
useQuery(queryKey, options?);
useQuery(queryKey, queryFn, options?);
type.d.tsファイルより、
queryKeyは文字列、配列で指定できる。
queryFnの戻り値は任意の型かPromiseの任意の型。
optionsはこちらを参照。
type.d.ts
export declare type QueryKey = string | readonly unknown[];
export declare type QueryFunction<T = unknown, TQueryKey extends QueryKey = QueryKey> = (context: QueryFunctionContext<TQueryKey>) => T | Promise<T>;
useQueryサンプルコード
コードはこちら
useTodoQuery.ts
// jsonplaceholderのtodosの型を定義
type TodoType = {
userId: Number,
id: Number,
title: String,
completed: boolean,
}
// queryFn
const fetchTodosQueryFn = async () => {
const result = await fetch('https://jsonplaceholder.typicode.com/todos');
if (result.status !== 200) {
throw new Error('fetch Error')
}
return result.json();
}
// useQUery(queryKey='todos' , queryFn=fetchTodosQueryFn)
export const fetchTodoList = () => {
return useQuery<TodoType[], Error>(
'todos',
fetchTodosQueryFn,
);
// オブジェクトとして書くことも可能
// return useQuery<TodoType[], Error>(
// {
// queryKey: 'todos',
// queryFn: fetchTodosQueryFn,
// }
// );
}
Todos.tsx
import { fetchTodoList } from 'useTodoQuery';
export const Todos = () => {
const { isLoading, isError, error, data } = fetchTodoList();
if (isLoading) {
return (
<>
'Loading...'
</>
);
}
if (isError) {
return (
<>
error : {error.message}
</>
);
}
if (!data) {
return (
<>
none data
</>
);
}
return (
<ul>
{data.map(todo => (
<li key={todo.id.toString()}>{todo.title}</li>
))}
</ul>
);
};
以下のように取得したtodoが表示されていることが確認できる。
useMutation
useMutationはデータの作成/更新/削除時に使用される。
useMutation.d.tsには以下の4つのuseMutation関数が記載されている。(一部省略)
useMutation.d.ts
useMutation(options);
useMutation(mutationFn, options?);
useMutation(mutationKey, options?);
useMutation(mutationKey, mutationFn?, options?);
useMutationサンプルコード
コードはこちら
useTodoQuery.ts
// jsonplaceholderのtodosの型を定義
type TodoType = {
userId: Number,
id: Number,
title: String,
completed: boolean,
}
const createTodoQueryFn = (body: NewTodoType) => axios.post<NewTodoType>('https://jsonplaceholder.typicode.com/todos', body).then(res => res.data);
const createTodoWithMutation = () => useMutation<NewTodoType, Error, NewTodoType>(
'newTodo',
async (body: NewTodoType) => {
const result = await createTodoQueryFn(body);
return result;
},
{
onSuccess: (todo) => {
console.log('Mutation', todo);
},
onError(error, variables, context) {
console.log('error', error.message);
},
}
);
TodoForm.tsx
import { Button, Box, Container, TextField, Typography } from '@mui/material';
import { SubmitHandler, useForm } from 'react-hook-form';
import * as yup from "yup";
import { yupResolver } from '@hookform/resolvers/yup';
import { useTodoList } from 'useTodoList';
// バリデーションルール
let todoSchema = yup.object({
id: yup.number().default(1),
title: yup.string().required('タイトルを入力してください'),
completed: yup.boolean().default(false),
});
export const TodoInputForm = () => {
const { createTodo, createTodoWithMutation } = useTodoList();
const mutation = createTodoWithMutation();
const {
register,
handleSubmit,
formState: { errors },
} = useForm<TodoType>({ resolver: yupResolver(todoSchema) })
const onSubmit: SubmitHandler<TodoType> = async (data) => {
mutation.mutate(
{
id: data.id,
title: data.title,
});
};
return (
<Container maxWidth="sm" sx={{ p: 5 }}>
<Typography variant='h1' >Todo List</Typography>
{mutation.isLoading ?
<>
作成中...
</>
:
<Box
sx={{
display: 'flex',
justifyContent: 'space-around',
}}
>
<TextField
required
label="タイトル"
type="title"
{...register('title')}
error={'title' in errors}
helperText={errors.title?.message}
/>
<Button
color="primary"
variant="contained"
size="large"
onClick={handleSubmit(onSubmit)}
>
新規作成
</Button>
</Box>
}
</Container>
);
};