本系列文以製作專案為主軸,紀錄小弟學習React以及GrahQL的過程。主要是記下重點步驟以及我覺得需要記憶的部分,有覺得不明確的地方還請留言多多指教。
Apollo Client 在進行 query 後會在 client 端建立 cache,而在 mutation 後若希望畫面更新到最新資訊,Apollo Client 自有一套更新的方法,在介紹方法前先用 dev tools 讓 cache 可視化,並介紹一下 Cache 的構造。
Apollo 提供的工具,安裝後在網頁工具中找到 Apollo
可以在 Cache 標籤看到目前的 cache 內容。
在Cache中可以看到像是 List:6 這樣的名稱,這是 InMemoryCache 這個方法幫每筆資料建立的
cache id,構造會像這樣:
[__typename]:[id]
這個 __typename 就是根據 Server 的 schema 建立的,Apollo Client 在 Query 時會自己偷偷請求這個欄位,可以在Queries標籤底下看到:
而每筆資料有了獨特id後就表列在 cache 中,至於資料間的關聯會以 __ref 參照:
要在mutation後觸發單一筆 cache 資料的更新,方法是在 mutation 的回完值中帶上目標的 id 以及更新的欄位,以下用editTodo為例。
先補上 Server端的 schema:
editTodo(todoId: Int, name: String): Todo!
以及 reducer:
editTodo: (parent, args, context) => {
return context.prisma.todo.update({
where: { id: args.todoId },
data: {
name: args.name,
},
});
}
Client 端 Mutation 請求:
const EDIT_TODO = gql`
mutation EditTodo($name: String!, $todoId: Int!) {
editTodo(name: $name, todoId: $todoId) {
id //目標的id
name //更新的欄位
}
}
`;
宣告 editTodo方法:
const [editTodo] = useMutation(EDIT_TODO);
這樣當 editTodo 被執行後如果成功更新並回傳,Apollo Client就會找到對應的 Todo:id 資料並更新 name 欄位,同時通知 React 刷新畫面。
如果新增了資料,在 Cache 中沒有對應的 id 可以觸發更新,就要用 cache.modify 方法。
以 addList 方法為例,先補上 server schema 跟 resolvers。
addList(listTitle: String): List!
addList: (parent, args, context) => {
return context.prisma.list.create({
data: {
title: args.listTitle,
},
});
}
Client 端 Mutation 請求:
const ADD_LIST = gql`
mutation AddList($listTitle: String!) {
addList(listTitle: $listTitle) {
id
}
}
`;
宣告 addList 方法:
const [addList] = useMutation(ADD_LIST, {
update(cache, { data: { addList } }) {
cache.modify({
fields: {
lists(
existingLists = [] //預設為空陣列
) {
const newListRef = cache.writeFragment({
data: addList,
fragment: gql`
fragment NewList on List {
id
title
}
`,
});
return [...existingLists, newListRef];
},
},
});
},
});
事情變得複雜了,一層層解釋吧。
useMutation 可接收兩個參數,第一個就是 gql指令,而第二個則是 option 物件。
option 中包含很多方法,像是 onError,onCompleted 等,可以更精確的操控 mutation 過程。
而 option 其中的 update 用於定義 mutation 完成,收到回傳值後操作 cache 的動作。
update( cache , respondedData ){...}
從 update 可以存取 cache 實例以及回傳的資訊,範例中 { data: { addList } } 只是利用解構賦值將回傳的 List 物件直接取出來用。
cache.modify 用於編輯已存在的 cache 資料,主要用以下參數指定編輯目標:
fields: {
lists(){...}
}
用於編輯目標 cache 的 lists 欄位。cache.modify 用 id 與 fields 指定好編輯目標後就撰寫Modifiers實作編輯動作。
Modifier 方法包含兩個參數,第一個是目標欄位的目前值,第二個是一組 helper function 工具。
以 lists為例的話:
lists( existingLists = [] , {readField}) {...}
在這個 Modifier 裡,首先要新增一個 newList cache,並將 newList 的參照添加到現有的 lists 陣列中。
const newListRef = cache.writeFragment({
data: addList,
fragment: gql`
fragment NewList on List {
id
title
}
`,
});
先說說 Fragment ,這是 GraphQL 語法的一部分,主要用來打包對象Type 的一組 Fields,並可重複利用。
fragment NewList on List {
id
title
}
NewList 只包含了 List 的 id 跟 title 欄位。
而 writeFragment 可以用 fragment 決定要寫入 cache 的 資料結構,並將 data 中的資料寫入 Cache,並回傳新的 cache 的 __ref。
Modifier 的回傳值會成為 cache.modify 對象的新值,這邊將新取得的 __ref 加入 lists 陣列回傳,就成了新增後的 lists。
這次先簡單示範兩個常用的 cache 編輯方式,還有很多應用可以研究。