1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【React】Suspenseで更新されたデータを表示する方法

Posted at

はじめに

Suspenseを使用したデータ取得・描画について、更新の際はどうすればよいのかだいぶ悩んだので、備忘録を兼ねて投稿します。

やりたかったこと

モーダルのフォームに記録を入力して、「登録」ボタンを押すとデータが登録され、画面に即時反映される。

StudyRecordApp_updateList.gif

コード

データ取得用カスタムフック

src/hooks/useFetchData.tsx
import { use } from "react";
import { GetRecords } from "../lib/record";
import { Record } from "../domain/record";

let recordsPromise: Promise<Record[]> | null = null;

export const useFetchData = () => {
  if (!recordsPromise) {
    recordsPromise = new Promise<Record[]>((resolve) => {
      (async () => {
        const data = await GetRecords();
        resolve(data);
      })();
    });
  }

  const records = use(recordsPromise);

  return { records };
};

export const refetchRecords = () => {
  recordsPromise = null;
};

モーダルコンポーネント

src/components/InputForm.tsx
import { ChangeEvent, useState } from "react";
import {
  Modal,
  ModalBody,
  ModalCloseButton,
  ModalContent,
  ModalFooter,
  ModalHeader,
  ModalOverlay,
  FormControl,
  FormLabel,
  Input,
  NumberInput,
  NumberInputField,
  Button,
} from "@chakra-ui/react";

import { AddRecord } from "../lib/record";
import { refetchRecords } from "../hooks/useFetchData";

type InputFormProps = {
  isOpen: boolean;
  onClose: () => void;
};

export const InputForm = ({ isOpen, onClose }: InputFormProps) => {
  const [title, setTitle] = useState("");
  const [time, setTime] = useState("");

  const onChangeTitle = (e: ChangeEvent<HTMLInputElement>) =>
    setTitle(e.target.value);
  const onChangeTime = (valueAsString: string) => setTime(valueAsString);

  const onClickAdd = () => {
    try {
      AddRecord(title, time);
      setTitle("");
      setTime("");
      onClose();
    } catch (error) {
      console.error(error);
    }
  };

  return (
 中略
          <Button onClick={onClickAdd} colorScheme="blue" mr={3}>
            登録
          </Button>

以下略

App.tsx

App.tsx
import { Suspense } from "react";
import { ErrorBoundary } from "react-error-boundary";
import { Button, Heading, useDisclosure } from "@chakra-ui/react";

import { RecordList } from "./components/RecordList";
import { InputForm } from "./components/InputForm";

function App() {
  const { isOpen, onOpen, onClose } = useDisclosure();
  return (
    <>
      <Heading as="h1" data-testid="title">
        学習記録アプリ
      </Heading>
      <ErrorBoundary
        fallback={
          <Heading as="h2" size="md">
            データ取得に失敗しました。
          </Heading>
        }
      >
        <Button onClick={onOpen} colorScheme="blue">
          登録
        </Button>
        <InputForm isOpen={isOpen} onClose={onClose} />
        <Suspense
          fallback={
            <Heading as="h2" size="md">
              Loading...
            </Heading>
          }
        >
          <RecordList />
        </Suspense>
      </ErrorBoundary>
    </>
  );
}

export default App;

解決策

SuspensePromiseが解決されると再レンダリングを行います。データ取得用カスタムフックは、キャッシュがない場合は新たなPromiseインスタンスを作成するので、キャッシュクリアする再取得用関数を入れてやればいいだけでした。

src/hooks/useFetchData.tsx
import { use } from "react";
import { GetRecords } from "../lib/record";
import { Record } from "../domain/record";

let recordsPromise: Promise<Record[]> | null = null;

export const useFetchData = () => {
  if (!recordsPromise) {
    recordsPromise = new Promise<Record[]>((resolve) => {
      (async () => {
        const data = await GetRecords();
        resolve(data);
      })();
    });
  }

  const records = use(recordsPromise);

  return { records };
};

// 再取得用関数
+ export const refetchRecords = () => {
+  recordsPromise = null;
+ };
src/components/InputForm.tsx
(前略

  const onClickAdd = () => {
    try {
      AddRecord(title, time);
      setTitle("");
      setTime("");
      onClose();
+     refetchRecords();
    } catch (error) {
      console.error(error);
    }
  };

以下略

おわりに

わかってしまうと簡単なのですが、stateで管理するという発想から抜け出せず、苦労しました。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?