86
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

アイレット株式会社Advent Calendar 2024

Day 5

Amplifyでデータをフィルタリングできないときの対処法

Last updated at Posted at 2024-12-04

データがフィルタリングできないのですが?!🤯

Amplifyでデータをフィルタリングしよう!
〜コード修正の巻〜

概要

未経験24新卒❤️‍🔥の筆者が、研修で初めて個人アプリ開発をすることになりました。
現在進行形でAmplifyを使用して日記アプリを作成中。
最初にクイックスタート↓を利用してTodoアプリを作成し日記アプリに改修しています。

指定した日付を参照してデータをフィルタリングして表示したい!のに……
つまづいてしまったので備忘録としてまとめました。
誰かの役に立てれば嬉しいです!

何についての記事?誰向け?

  • Amplifyで取得したデータをフィルタリングして表示する方法
  • AmplifyやTypeScriptが初めての駆け出しエンジニア向け

今回行いたいデータ処理については公式ドキュメントに記載があるのですが……
ドキュメントを読んだだけでは実装できな〜〜い!

修正版のコードや基礎知識をまとめてみたので、筆者と同じようにドキュメントが読めないよ🫠という方でも安心(?)

有識者の方々のアドバイスもコメントでお待ちしております🫡

公式ドキュメントはこちら↓

https://docs.amplify.aws/react/build-a-backend/data/query-data/

Amplifyでデータをフィルタリングできない原因は3点あった

原因はいろいろあるが主に3点

  • データをフィルタリングする処理が終わる前に次の処理が進んでしまった
    →Promise, await

  • filter条件をeq: "2024-12-05"などとしていてDBの値と一致していなかった

  • 非同期処理などの基礎知識の理解不足

改修前のコード:何を考えていたか

Diary.tsx
const Diary: React.FC = () => {
    const [todos, setContent] = useState<Array<Schema["Todo"]["type"]>>([]);
    const targetDate = "2024-11-18";

    const result = client.models.Todo.list({
        filter: {
           createdAt: { eq: targetDate }, // targetDate に一致するデータを取得
            },
        });

やろうとしたこと

  • tagetDateに一旦固定で日付を設定
  • resultでフィルター
  • createdAtがtagetDateと一致するデータを取得する

修正したらフィルタリングできた

Diary.tsx
const Diary: React.FC<CalenderProps> = ({ date }) => {
    const [todos, setContent] = useState<Array<Schema["Todo"]["type"]>>([]);
    async function fetchTodos() {
        const targetDate = "2024-11-27"
        const result = await client.models.Todo.list(
            {
                filter: {
                    createdAt: {beginsWith: targetDate},
                },
            }
        );
        console.log("=== " + JSON.stringify(result))
        setContent([...result.data]);
    }

修正点

① client.models.Todo.listにawaitをつける

→DBからデータを取得するメソッド
戻り値がPromiseなので処理が完了するまで待つ必要がある
→Promiseって?
Promiseの処理が完了するまで待つにはawaitをつけて実行
awaitを使用するにはasyncで囲むことが必須!


② {beginsWith: targetDate}でフィルターする

targetDateにはxxxx-xx-xxという値で日付情報が入る。これを使用してフィルタリングする
しかしDBに入っている日付のデータは"2024-11-18T08:59:21.189Z” なのでeqだとヒットしなくてフィルターできない
{beginsWith: targetDate} にすることで指定した日付で部分検索?ができる!


③ filter結果resultを確認できるようにする

console.log("=== " + JSON.stringify(result))resultの中身をコンソールに出力

{"data":[{"id":"******","content":"あ!","comment":null,"createdAt":"2024-11-27T09:47:52.337Z","updatedAt":"2024-11-27T09:47:52.337Z","owner":"******"}],"nextToken":null}

④ フィルタリング結果を表示

setContent([...result.data]);でフィルターされた結果をTodosに保存
→表示したいときはTodos.contentなどで指定できるよ

初心者必見!Amplifyでデータをフィルタリングするために必要な超基礎知識

client.models.Todo.list とはDBからデータを取得するメソッド

AWS AmplifyのDatastoreまたはGraphQL APIを使用してデータベースからデータを取得するためのメソッド

client

Amplifyのクライアントオブジェクト。これを通じてAmplifyが生成したデータモデルにアクセスする

Diary.tsx
import { type Schema } from '../amplify/data/resource';
const client = generateClient<Schema>();
//(↑ここで宣言したclient)

resourceファイルで定義したDB情報をインポート

../amplify/data/resource
const schema = a.schema({
 Todo: a.model({
   content: a.string(),
   comment: a.string(),
 }).authorization(allow =>  [allow.owner()]),

generateClient()ではSchemaという型のデータを渡してその構造に合わせた機能を利用できるようにクライアントを生成している
→このおかげでデータを操作するためのメソッドが利用できるよ!

models

データの構造を予め決めている

models: ModelTypes<{
…
Todo: ModelType<SetTypeSubArg<{
               fields: {
                   content: ModelField<Nullable<string>, never, undefined>;
                   comment: ModelField<Nullable<string>, never, undefined>;
               };
…

今回は上記のようにcontentとcommentには文字列が入りかつnullを許容するということを定義している

Todo

モデルに基づいた実際のデータの集合
DB名が入る

list

listはTodoモデルのデータをリスト形式で取得する操作
他にも色々な操作ができるメソッドがある

メソッド 機能
list データ一覧を取得
get 指定したIDのデータを取得
create 新しいデータの登録
update 既存データの更新
delete 既存データの削除
query GraphQLクエリを実行し、データを検索
observe データの変更をリアルタイムで監視

Promiseで非同期処理の結果を管理しよう

javascriptは非同期言語であるため、実行完了を待たずに次の処理が行われる
→書かれた通りに処理をしてくれない

同期言語:書かれた通りに処理を進める。

関数が実行されている間はプログラムが無反応になる
Promiseを使うことで処理の順序を指定できる

PromiseStatus

ステータス 意味
pending 未解決(非同期処理がまだ完了していない)
resolved 解決済み(正常に完了し結果が利用可能)
rejected 拒否(失敗しエラーが発生)

今回のコードのclient.models.Todo.list()では、データベースにリクエストを送り、データが返ってくるまでPending状態。
resultにはPromiseオブジェクトが格納される→データが利用不可のままコードが進んでしまう

どうする? →awaitを使う:データをデータを取得するまで待ってくれる!

await, asyncで結果が出るまで待とう

Promiseによる非同期処理をより簡潔に効率よく記述できる!

Promiseチェーン

client.models.Todo.list({
  filter: {
    createdAt: { beginsWith: targetDate },
  },
})
  .then((result) => {
    console.log("=== フィルター結果: ", JSON.stringify(result));
    // 必要に応じて他の処理を追加
  })

client.models.Todo.listはPromiseを返すためthenを使って結果を処理
これだけだとあまり変わらないように見えるが、複数の非同期処理を書こうとすると可読性が低下する

await

const result = await client.models.Todo.list({
		filter: {
				createdAt: {beginsWith: targetDate},
	},
 });
	console.log("=== " + JSON.stringify(result))

同期処理のように書けるため可読性が高い!
async:関数の頭につけることでPromiseオブジェクトを返す関数にできる
await:Promiseオブジェクトが値を返すのを待つ演算子(asyncの中で使う!)

非同期関数で他の処理も同時で実行しよう

同期関数:長時間実行される関数の場合、他の操作が一切できなくなる
その点非同期関数は

  • 関数が処理を開始してすぐに値を返す→プログラムが他の処理を実行できる
  • 最終的に処理が完了したらその結果を通知

と便利

まとめ

公式ドキュメント、色々な記事を読んでも、AIに聞いても解決しないことってたくさんありますよね。
今回の処理も結局一人では無理で先輩にアドバイスをもらってやっと動きました😗

初歩的なことではあるんですが、返り値の理解とか、どこで使うべきかちゃんと理解できていなかったのが原因だと思います。特にPromiseや日付の処理について曖昧なまま進めてしまいました。

本記事ではドキュメントを読み解くための超❗初心者向け基礎知識を整理しました。
この内容を調べたことで、少しドキュメントを読めるようになった……🦆

記事のタイトルでは大げさに「〜〜の方法!」とうたっていますが、実際には開発中につまづいた部分を備忘録的にまとめたものです。どなたかのお役に立てば幸いです💗

おまけ

Diary.tsx
    async function fetchTodos() {
        const targetDate = "2024-11-27"
        const result = await client.models.Todo.list(
            {
                filter: {
                    createdAt: {beginsWith: targetDate},
                },
            }
        );
        console.log("=== " + JSON.stringify(result))
        // setContent([...result.data]);
    }
    fetchTodos()

レンダリングとは何なのか?を理解できた失敗

// setContent([...result.data]);のコメントアウトを解除すると```console.log("=== " + JSON.stringify(result))``が大量に表示されるようになる
→fetchTodos 関数の実行が 無限ループ のように繰り返されている

setContent([...result.data]) の呼び出しによって、React コンポーネントが再レンダリングされ、その中で fetchTodos が再度実行される構造

fetchTodosがコンポーネントのレンダリング中に呼びだされている
→直接関数を呼び出すように書いているから

setContentが状態を更新するたびにコンポーネントが再レンダリングされてfetchTodosが再実行される無限ループに
→なぜsetContentが状態を更新し続けてしまうのか?

これ!

状態の更新が再レンダリングを引き起こす
fetchTodos関数が実行される
→setContentが呼び出される
→コンポーネントが再レンダリングされる
→fetchTodos関数が実行される
→以下無限ループ

❤️‍🔥 useEffect内にfetchTodosを記述することで解決 ❤️‍🔥

86
4
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
86
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?