TECH BLOG

Use Mutations - Blitz.js日本語訳ドキュメント

2022-08-15 18:35:50 +09:00

Blitz.jsReactJavaScript

Using Mutations

ミューテーションの使用

Reactコンポーネント内での使用

Blitzミューテーションを使用するには、useMutation フックを自作のミューテーションリゾルバと一緒に呼び出します。

useMutation は、 [invoke, mutationExtras]というタプル配列を返します。invokeはイベントハンドラや副作用(effect)の中で直接呼び出すことができる非同期関数です。

useMutation は、react-query が提供する useMutation フックの薄いラッパーです。

import {useMutation} from '@blitzjs/rpc'
import updateProject from 'app/projects/mutations/updateProject'

function (props) {
  const [updateProjectMutation] = useMutation(updateProject)

  return (
    <Formik
      onSubmit={async values => {
        try {
          const project = await updateProjectMutation(values)
        } catch (error) {
          alert('Error saving project')
        }
      }}>
      {/* ... */}
    </Formik>
  )
}

詳細は、 useMutation documentation をご覧ください。

🤔サーバーコードをコンポーネントにインポートして動くのが不思議かもしれません。サーバー側の関数を直接インポートしている箇所はビルド時にネットワーク呼び出しに置き換えられます。つまり、これらのコードがクライアントコードに含まれることはありません。

エラーハンドリング

サーバーコードからスローされたエラーは、そのままクライアントでスローされます。そのため、try {} catch {} または .catch()invoke を括ることができます。

キャッシュの無効化

setQueryData()

  • useQuery は、キャッシュを手動で更新するために使用できる setQueryData 関数を返します。
  • setQueryData を呼び出したあと、データの整合性をとるために自動的にリフェッチが行われます。第2引数に {refetch: false} を渡すことで、リフェッチを無効にできます。
export default function (props: { query: { id: number } }) {
  const [product, { setQueryData }] = useQuery(getProduct, {
    where: { id: props.query.id },
  })
  const [updateProjectMutation] = useMutation(updateProject)

  return (
    <Formik
      initialValues={product}
      onSubmit={async (values) => {
        try {
          const product = await updateProjectMutation(values)
          setQueryData(product)
        } catch (error) {
          alert("Error saving product")
        }
      }}
    >
      {/* ... */}
    </Formik>
  )
}

refetch()

useQuery は、クエリのリロードをトリガするための refetch 関数を返します。

export default function (props) {
  const [product, { refetch }] = useQuery(getProduct, {
    where: { id: props.id },
  })
  const [updateProjectMutation] = useMutation(updateProject)

  return (
    <Formik
      initialValues={product}
      onSubmit={async (values) => {
        try {
          const product = await updateProjectMutation(values)
          refetch()
        } catch (error) {
          alert("Error saving product")
        }
      }}
    >
      {/* ... */}
    </Formik>
  )
}

keywords: refresh, revalidate, invalidate

invalidateQuery()

また、他のコンポーネント内からクエリを無効にする必要がある場合は、blitz が提供する invalidateQuery() 関数を使用することもできます。

Example

import { invalidateQuery } from "@blitzjs/rpc"import getProducts from "app/products/queries/getProducts"

const Page = function ({ products }) {
  return (
    <div>
      <button
        onClick={() => {
          invalidateQuery(getProducts)        }}
      >
        Invalidate
      </button>
    </div>
  )
}
export default Page

自動化

どうにかしてクエリキャッシュを自動的に無効にしたいのですが、どうやればいいかまだわかっていません。オープンイシューを用意していますので、ぜひご協力をお願いします。

楽観的アップデート (Optimistic Updates)

楽観的UIパターンは、ユーザーのインタラクションに対するアプリの応答性を高めるための手法です。ユーザーが何かアクションを起こした瞬間(たとえば、投稿に「いいね!」を押したとき)、そのアクションが成功したと仮定することで、スピナーやローディングアニメーションの表示を避けることができます。すぐに成功した結果を表示するために、ローカルキャッシュとインターフェースが直ちに更新されます。

実際のレスポンスが到着すると、データベースから返された真の値を反映してUIを更新します。ほとんどの場合、リクエストが成功していれば、この動作はユーザーには見えず、変更は即座に完了したように見えます。

エラーが発生した場合は、ローカルの変更を戻し、データを再取得し、オプションでエラーメッセージを表示します。

以下は、 onSubmit で、編集内容を保存するための updateProduct ミューテーションを呼び出すシンプルなフォームコンポーネントの例です。

export default function (props: { query: { id: number } }) {
  const [product, { setQueryData }] = useQuery(getProduct, {
    where: { id: props.query.id },
  })
  const [updateProjectMutation] = useMutation(updateProject)

  return (
    <Formik
      initialValues={product}
      onSubmit={async (values) => {
        try {
          const product = await updateProjectMutation(values)
          setQueryData(product)
        } catch (error) {
          alert("Error saving product")
        }
      }}
    >
      {/* ... */}
    </Formik>
  )
}

データベースに送信しようとしているデータを使って getProducts クエリのローカルキャッシュを変異させることにより、編集された製品情報をすぐに表示して、ユーザに即時フィードバックすることができます。setQueryData()関数の第2引数に {refetch: false} を渡すことで、更新が終わる前に古いデータを取得してしまうのを防ぎます。

export default function (props: { query: { id: number } }) {
  const [product, { setQueryData }] = useQuery(getProduct, {
    where: { id: props.query.id },
  })
  const [updateProjectMutation] = useMutation(updateProject)

  return (
    <Formik
      initialValues={product}
      onSubmit={async (values) => {
        try {
          // Update local cache with form values without triggering refetch
          setQueryData(values, { refetch: false })          const product = await updateProjectMutation(values)
          // Update local cache with result, then refetch
          setQueryData(product)
        } catch (error) {
          // Set cache back to original result, then refetch
          setQueryData(product)          alert("Error saving product")
        }
      }}
    >
      {/* ... */}
    </Formik>
  )
}
© 2020 SEMI