新しいtodoとの同期

公開リストに新しく入力されたtodoをUIで表示できるようにします。UIが自動的にtodoを表示する代わりに、新しいtodoを受信するたびに表示される通知バナーのようなフィードを使用します。

以前、キャッシュAPIを使ってキャッシュを更新すると、UIが自動更新されたことを思い出してください。これは、キャッシュの更新によって、このストアにサブスクライブしているコンポーネントの再レンダリングがトリガーされたためです。

今回はUIによる公開リストの自動更新は必要ないため、この方法は使用しません。

前のステップの TodoPublicListSubscription メソッドでは、最新のtodoだけを取得して、既存のtodoリストは取得しませんでした。ここでは、既存のパブリックtodoのリストを取得するqueryを記述します。

まず、 react から useEffect を、 @apollo/client から useApolloClient をimportすることから始めます。

- import React, { Fragment } from "react";
+ import React, { Fragment, useState, useEffect } from "react";
- import { useSubscription, gql } from "@apollo/client";
+ import { useSubscription, useApolloClient, gql } from "@apollo/client";
import TaskItem from "./TaskItem";

これで、clientにアクセスできるようになったので、 TodoPublicList コンポーネントを更新します。

const TodoPublicList = props => {
- const state = {
+ const [state, setState] = useState({
- olderTodosAvailable: true,
+ olderTodosAvailable: props.latestTodo ? true : false,
- newTodosCount: 1,
+ newTodosCount: 0,
todos: [
- {
- id: "1",
- title: "This is public todo 1",
- user: {
- name: "someUser1"
- }
- },
- {
- id: "2",
- title: "This is public todo 2",
- is_completed: false,
- is_public: true,
- user: {
- name: "someUser2"
- }
- },
- {
- id: "3",
- title: "This is public todo 3",
- user: {
- name: "someUser3"
- }
- },
- {
- id: "4",
- title: "This is public todo 4",
- user: {
- name: "someUser4"
- }
- }
],
+ error: false
- };
+ });
+ let numTodos = state.todos.length;
+ let oldestTodoId = numTodos
+ ? state.todos[numTodos - 1].id
+ : props.latestTodo
+ ? props.latestTodo.id + 1
+ : 0;
+ let newestTodoId = numTodos
+ ? state.todos[0].id
+ : props.latestTodo
+ ? props.latestTodo.id
+ : 0;
+
+ const client = useApolloClient();
}
const loadNew = () => {};
const loadOlder = () => {};
...
}

useEffect で既存のtodoリストを取得して、initial stateを生成します。

const TodoPublicList = props => {
...
const client = useApolloClient();
+ useEffect(() => {
+ loadOlder();
+ }, []);
const loadNew = () => {};
const loadOlder = () => {}
...
}

loadOlder メソッドを以下の通り更新します。

const loadOlder = async () => {
+ const GET_OLD_PUBLIC_TODOS = gql`
+ query getOldPublicTodos ($oldestTodoId: Int!) {
+ todos (where: { is_public: { _eq: true}, id: {_lt: $oldestTodoId}}, limit: 7, order_by: { created_at: desc }) {
+ id
+ title
+ created_at
+ user {
+ name
+ }
+ }
+ }`;
+
+ const { error, data } = await client.query({
+ query: GET_OLD_PUBLIC_TODOS,
+ variables: { oldestTodoId: oldestTodoId }
+ });
+
+ if (data.todos.length) {
+ setState(prevState => {
+ return { ...prevState, todos: [...prevState.todos, ...data.todos] };
+ });
+ oldestTodoId = data.todos[data.todos.length - 1].id;
+ } else {
+ setState(prevState => {
+ return { ...prevState, olderTodosAvailable: false };
+ });
+ }
+ if (error) {
+ console.error(error);
+ setState(prevState => {
+ return { ...prevState, error: true };
+ });
+ }
+ }

古い公開todoを取得するqueryを定義し、 client.query を呼び出してデータベースからデータを取得します。データを取得したら、 todos stateを更新して、public todoの利用可能なリストを使ってUIを再レンダリングします。

公開フィードに新しいtodoを追加してみましょう。新しいtodoがUIに表示されないことを確認してください。ページを更新するとtodoが追加されることを確認してください。

これは、新しく追加されたtodoをフィードして表示する仕組みがまだ実装されてないからです。

この仕組みは、更新で使用している useEffect で処理します。

useEffect(() => {
loadOlder();
}, []);
+ useEffect(
+ () => {
+ if (props.latestTodo && props.latestTodo.id > newestTodoId) {
+ setState(prevState => {
+ return { ...prevState, newTodosCount: prevState.newTodosCount + 1 };
+ });
+ newestTodoId = props.latestTodo.id;
+ }
+ },
+ [props.latestTodo]
+ );

公開フィードに新しいtodoを追加してみましょう。新しいタスクの受信通知が表示されるはずです。

完璧です。まだ実装していない機能が一つ残っています。新しいタスクが公開フィードに届いて、ユーザーがNew tasks sectionをクリックしたときに、現在のパブリックフィードにないtodoを再取得するqueryを作成する必要があります。

loadNew() メソッドを以下のコードに更新します。

const loadNew = async () => {
+ const GET_NEW_PUBLIC_TODOS = gql`
+ query getNewPublicTodos ($latestVisibleId: Int!) {
+ todos(where: { is_public: { _eq: true}, id: {_gt: $latestVisibleId}}, order_by: { created_at: desc }) {
+ id
+ title
+ created_at
+ user {
+ name
+ }
+ }
+ }
+ `;
+
+ const { error, data } = await client.query({
+ query: GET_NEW_PUBLIC_TODOS,
+ variables: {
+ latestVisibleId: state.todos.length ? state.todos[0].id : null
+ }
+ });
+ if (data) {
+ setState(prevState => {
+ return {
+ ...prevState,
+ todos: [...data.todos, ...prevState.todos],
+ newTodosCount: 0
+ };
+ });
+ newestTodoId = data.todos[0].id;
+ }
+ if (error) {
+ console.error(error);
+ setState(prevState => {
+ return { ...prevState, error: true };
+ });
+ }
}
Did you find this page helpful?
Start with GraphQL on Hasura for Free
  • ArrowBuild apps and APIs 10x faster
  • ArrowBuilt-in authorization and caching
  • Arrow8x more performant than hand-rolled APIs
Promo
footer illustration
Brand logo
© 2024 Hasura Inc. All rights reserved
Github
Titter
Discord
Facebook
Instagram
Youtube
Linkedin
graphql-handbook