摘要:分鐘學(xué)是一個系列,簡單暴力,包學(xué)包會。一旦組件掛載后,會自動進(jìn)行數(shù)據(jù)請求,前提是客戶端提供的和后端的相符。如果回調(diào)返回直接不作請求。在組件內(nèi)進(jìn)行分頁請求之前提到了,這個裝飾器為添加了對象,其中有個函數(shù)為。
使用 Apollo 獲取數(shù)據(jù)21 分鐘學(xué) apollo-client 是一個系列,簡單暴力,包學(xué)包會。
搭建 Apollo client 端,集成 redux
使用 apollo-client 來獲取數(shù)據(jù)
修改本地的 apollo store 數(shù)據(jù)
提供定制方案請求攔截
封裝修改 client 的 api
apollo store 存儲細(xì)節(jié)
寫入 store 的失敗原因分析和解決方案
推薦先看:GraphQL 入門: 連接到數(shù)據(jù)
本文只做補(bǔ)充。
下面編寫一個最簡單的 Container,觀察是否能 query 到數(shù)據(jù)。
container.jsx
import React, { PureComponent } from "react"; import { graphql } from "react-apollo"; import query from "./query.gql"; @graphql(query) export default class ApolloContainer extends PureComponent { render() { console.log(this.props); returnHello Apollo; } }
@graphql(query) 是 apollo 提供的高階組件,以裝飾器的形式包裹你的組件。這里是最簡單的情況,只傳一個 query。
query 語法基本的 query 語法可以參看官方文檔 Queries and Mutations | GraphQL,這里提一下 Apollo 特有的一些語法。
query.gql
#import "../gql/pageInfo.gql" #import "@/gql/topic/userTopicEntity.gql" query topic($topicId: Int!, $pageNum: Int = 1) { community { topicEntity { listByTopicId(topicId: $topicId, pageSize: 10, pageNum: $pageNum) { pageInfo { ...pageInfo } edges { ...userTopicEntity } } } } }
前兩行 import 了其它的 fragment。想必你已經(jīng)知道,GraphQL 主要通過 fragment 來組合分形 Query。一個好的實(shí)踐是盡量對業(yè)務(wù)實(shí)體編寫 fragment 以便復(fù)用。
代碼脫敏的關(guān)系我就不放詳細(xì)的 fragment 了。
上一節(jié)我們在 webpack 中配置了 graphql-tag/loader,這個 loader 允許你將 query 、fragment 這些 schema 字符串,以 .gql 文件的形式保存,在 import 時轉(zhuǎn)化成 js 代碼。
其余部分,基本上和 GraphQL 原生寫法是一樣的,注意幾個點(diǎn):
一次請求只能包含一個 query,而且不能包含未使用的 fragment。
#import 語法是 loader 提供的,語法和 js 的 import 差不多,除了不能解構(gòu) 。
如果你 webpack 配置了 alias 就能使用第二行那種寫法。注意,它會把該文件內(nèi)所有的內(nèi)容都 import 進(jìn)來,所以不能在一個 gql 文件里寫多個 query 或 fragment。
對了,為了最小化實(shí)踐,你可以先寫不帶參數(shù)的 query。也先不要寫 union type。
props.data 的數(shù)據(jù)結(jié)構(gòu)這樣就好了嗎,是的。一旦組件掛載后,會自動進(jìn)行數(shù)據(jù)請求,前提是客戶端提供的 query schema 和后端的相符。
如果請求成功后,會發(fā)生什么事情呢?我們可以查看 this.props 打出的 log 來驗(yàn)證:
// this.props { // .... data: { // ... community: { ... }, // 這是獲取到的數(shù)據(jù),結(jié)構(gòu)和你提供的 query schema 一致 loading: false, // 請求過程中為 true networkStatus: 7, // 從 0-8,具體值的含義看這個文件 https://github.com/apollographql/apollo-client/blob/master/src/queries/networkStatus.ts variables: { ... }, // 請求時所用的參數(shù) fetchMore, // 一個函數(shù),用于在組件內(nèi)「繼續(xù)請求」,一般用于分頁請求 refetch, // 函數(shù),用于組件內(nèi)「強(qiáng)制重新請求」 updateQuery, // 請求成功后立即調(diào)用,用于更新本地 store } }高級請求
我們僅改寫裝飾器部分
@graphql(query, { skip: props => !isValid(props), options: props => ({ variables: { topicId: getIdFromUrl(), }, }), })
其中
skip 和 shouldComponentUpdate 的效果是一樣的,決定是否 re-fetch。如果回調(diào)返回 false 直接不作請求。
options 返回一個函數(shù),用以設(shè)置請求的細(xì)節(jié),比如 variables 用于設(shè)置 query 參數(shù)
更詳細(xì)的文檔可以查閱
API: graphql container with queries | Apollo React Docs
GraphQL 入門: Apollo Client - 連接到數(shù)據(jù)
分頁請求如文檔 Pagination | Apollo React Docs 所說,Apollo 支持兩種分頁
offset-based按條數(shù)偏移量來請求分頁,請求時提供兩個參數(shù)
limit:相當(dāng)于 pageSize,一頁最多取多少個
offset: 條數(shù)偏移量,第 n 頁的 offset = limit * n
可見你需要自己維護(hù)一個 pageNum: n 來實(shí)現(xiàn)按頁碼分頁
cursor-based這是 Relay 風(fēng)格的請求,cursor 用于記錄下個請求開始時,返回的第一個元素的位置,一般可以用該元素的 id 來標(biāo)識。
RESTful 風(fēng)格我們后端并沒有采取上面任何一種,而是提供了一個 pageInfo 對象,由前端傳入所需參數(shù),保持和 RESTful api 相似的風(fēng)格。
query.gql
#import "../gql/pageInfo.gql" #import "@/gql/topic/userTopic.gql" query topic($topicId: Int!, $pageNum: Int = 1) { community { topicEntity { listByTopicId(topicId: $topicId, pageSize: 10, pageNum: $pageNum) { pageInfo { ...pageInfo } edges { ...userTopicEntity } } } } }
pageInfo.gql
fragment pageInfo on PageInfo { pageNum # 頁碼 pageSize # 每頁條數(shù) pages # 總頁數(shù) total # 總條數(shù) }
聲明下,由于我們只使用 GraphQL 的 Query 功能,所以沒研究過這種格式是否會影響 Mutation。現(xiàn)在或以后有 Mutation 需求的,盡量采用官方推薦的前兩種吧。
在組件內(nèi)進(jìn)行分頁請求之前提到了, graphql 這個裝飾器為 this.props 添加了 data 對象,其中有個函數(shù)為 fetchMore。
fetchMore 看名字就知道是用來作分頁請求的。
下面我們看一個比較真實(shí)的例子,許多業(yè)務(wù)相關(guān)的代碼都用表示其作用的函數(shù)替代了,注意看注釋:
import React, { PureComponent } from "react"; import { graphql } from "react-apollo"; import { select } from "./utils"; // 注意,這里用的 query 是 「RESTful 風(fēng)格」那一節(jié)中貼出的 schema import query from "./query.gql"; @graphql(query, { skip: props => !isValid(props), options: props => ({ variables: { topicId: getIdFromUrl(), }, }), }) @select({ // 你可以寫一個函數(shù),從 this.props.data 里過濾出當(dāng)前列表的 pageInfo,直接添加到 this.props.pageInfo pageInfo: getPathInfoFromProps(props), }) export default class TopicListContainer extends PureComponent { hasMore = () => { const { pageNum = 0, pages = 0 } = this.props.pageInfo || {}; return pageNum < pages; } loadNextPage = () => { const { pageInfo = {}, data } = this.props; const { pageNum = 1 } = pageInfo; const fetchMore = data && data.fetchMore; if (!this.hasMore()) return; if (!fetchMore) return; return fetchMore({ variables: { // 是的,這里不需要把你在 `@graphql` 裝飾器中定義的其它 variables 再寫一遍 // apollo 會自動 merge pageNum: pageNum + 1, }, // 這個回調(diào)函數(shù),會在 fetch 成功后自動執(zhí)行,用于修改本地 apollo store updateQuery: (prev, { fetchMoreResult }) => { if (!fetchMoreResult) return prev; // 嘗試 log 下 `fetchMoreResult`,其返回的數(shù)據(jù)結(jié)構(gòu),和 query 中的 schmea 是一致的 // parseNextData 返回新數(shù)據(jù)。 // 新數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)必須和 query schema 一樣 // NOTE: 此處會有大坑,如果你發(fā)現(xiàn)最終數(shù)據(jù)并未改變,請閱讀后文 return parseNextData(prev, fetchMoreResult); } }); } render() { return (); } }
updateQuery 中,使用 parseNextData 經(jīng)過一些處理,返回新數(shù)據(jù)給 apollo,apollo 將把它寫入到 apollo store 中。
注意,這里至少會有兩處大坑
如果寫入失敗,是會靜默失敗的,也就是說 沒有任何報錯提示
如果寫入數(shù)據(jù)的結(jié)構(gòu),和 query schema 不符,就會寫入失敗。
但寫入失敗的情況還不止于此!如果你發(fā)現(xiàn)最終數(shù)據(jù)并未改變,可能是中招了,解毒方案 請閱讀 寫入 store 的失敗原因分析和解決方案
這段代碼只演示了如何 被動 地去修改本地的 apollo store 數(shù)據(jù),要問如何 主動 去修改 apollo store,請看這篇文章: 修改本地的 apollo store 數(shù)據(jù)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/91924.html
摘要:分鐘學(xué)是一個系列,簡單暴力,包學(xué)包會。那怎么辦呢本章就教你非常簡單地實(shí)現(xiàn)擴(kuò)展的。我們可以借鑒的的寫法,為的實(shí)例添加一些自己的方法。更重要的是,也會有的效果,上一個的輸出會成為下一個的輸入,便于組合。 21 分鐘學(xué) apollo-client 是一個系列,簡單暴力,包學(xué)包會。 搭建 Apollo client 端,集成 redux使用 apollo-client 來獲取數(shù)據(jù)修改本地的 ...
摘要:分鐘學(xué)是一個系列,簡單暴力,包學(xué)包會。其中提到了等需要后端配合的東西,徒增了配置的復(fù)雜性。如果不行,再跟隨我的簡單步驟試試。環(huán)境要求請確保你已經(jīng)搭建了自己的環(huán)境下文在行號前添加表示刪除的原代碼,表示新增的代碼。 21 分鐘學(xué) apollo-client 是一個系列,簡單暴力,包學(xué)包會。 搭建 Apollo client 端,集成 redux使用 apollo-client 來獲取數(shù)據(jù)...
摘要:分鐘學(xué)是一個系列,簡單暴力,包學(xué)包會。一旦你丟失了,可能會導(dǎo)致寫入失敗,或者盡管寫入了,但本該攜帶的那一層的數(shù)據(jù)沒有寫入。 21 分鐘學(xué) apollo-client 是一個系列,簡單暴力,包學(xué)包會。 搭建 Apollo client 端,集成 redux使用 apollo-client 來獲取數(shù)據(jù)修改本地的 apollo store 數(shù)據(jù)提供定制方案 請求攔截 封裝修改 clie...
摘要:分鐘學(xué)是一個系列,簡單暴力,包學(xué)包會。內(nèi)部通過自己的私有沒有暴露給開發(fā)者來更新這個。相當(dāng)于這個就是自己維護(hù)的,它將所有通過得到的數(shù)據(jù)保存在這里。的生成規(guī)則根據(jù)官方文檔的說法,在創(chuàng)建時,可選設(shè)置。如果不存在,則可能出現(xiàn)。 21 分鐘學(xué) apollo-client 是一個系列,簡單暴力,包學(xué)包會。 搭建 Apollo client 端,集成 redux使用 apollo-client 來...
摘要:分鐘學(xué)是一個系列,簡單暴力,包學(xué)包會。接管了請求和狀態(tài)管理。一般在生產(chǎn)環(huán)境中,我們通常還希望做權(quán)限驗(yàn)證請求攔截等事務(wù)處理。 21 分鐘學(xué) apollo-client 是一個系列,簡單暴力,包學(xué)包會。 搭建 Apollo client 端,集成 redux使用 apollo-client 來獲取數(shù)據(jù)修改本地的 apollo store 數(shù)據(jù)提供定制方案 請求攔截 封裝修改 clie...
閱讀 2376·2021-09-22 15:15
閱讀 640·2021-09-02 15:11
閱讀 1784·2021-08-30 09:48
閱讀 1884·2019-08-30 15:56
閱讀 1480·2019-08-30 15:52
閱讀 2042·2019-08-30 15:44
閱讀 431·2019-08-29 16:29
閱讀 1538·2019-08-29 11:06