Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Data feching and caching on Apollo Client

joe_re
September 15, 2017

Data feching and caching on Apollo Client

2017/09/15 ToKyoto.js LT

joe_re

September 15, 2017
Tweet

More Decks by joe_re

Other Decks in Technology

Transcript

  1. Data feching and
    caching on Apollo Client
    2017/09/15 ToKyoto.js LT
    @joe_re

    View Slide

  2. Who am I?
    twitter: @joe_re
    github: @joe­re
    working in freee.K.K
    GraphQL Tokyo Organizer

    View Slide

  3. What is Apollo Client?
    GraphQL
    のClient
    ライブラリ
    React
    、Angular
    、Vue
    、NativeApp(ReactNative, iOS,
    Android)
    などなど幅広くサポート
    対抗馬はFacebook
    製のRelay

    View Slide

  4. vs Relay

    View Slide

  5. の話はしないけどざっくりApollo
    が優位なところをざっくり
    サポートはReact, ReactNative
    のみ
    GraphQL API
    に制約をかける
    規約が多いので理解するまで大変
    (
    ただし慣れればパフォーマンスやDE
    は向上する)

    View Slide

  6. Fundations of
    GraphQL

    View Slide

  7. What is GraphQL
    Facebook
    が公開しているAPI
    の仕様
    データの取得、更新を行うクエリ言語を提供する
    特定の言語やフレームワークを指すものではない

    View Slide

  8. Example
    Query
    query {
    repository(owner: "apollographql", name: "apollo-client") {
    name,
    description,
    stargazers {
    totalCount
    }
    }
    }
    Result
    {
    "data": {
    "repository": {
    "name": "apollo-client",
    "description": ":rocket: A fully-featured, production ready caching GraphQL client for every
    server or UI framework",
    "stargazers": {
    "totalCount": 3948
    }
    }
    }
    }

    View Slide

  9. Good Points
    クライアント側で必要なデータを細かく取捨選択できるの
    で無駄がない
    複数のリソースを1
    つのリクエストで一度に得られる
    クライアントが理解できるスキーマを元にクエリするの
    で、期待した結果を得られる(
    型定義も容易)
    (http://graphql.org/)

    View Slide

  10. Three types of GraphQL operation
    Query:
    データの取得
    Mutation: Query
    で取得したデータの更新
    Subscription:
    データの変更の購読(Response Stream)

    View Slide

  11. Example of Query
    query SearchRepository($queryString: String!, $cursor: String) {
    search(query: $queryString, type: REPOSITORY, first: 30, after: $cursor) {
    repositoryCount
    edges {
    cursor
    node {
    ... RepositorySearchResult
    }
    }
    }
    }
    fragment RepositorySearchResult on Repository {
    databaseId
    name
    owner {
    avatarUrl(size: 40)
    login
    }
    description
    stargazers {
    totalCount
    }
    forks {
    totalCount
    }
    updatedAt
    }

    View Slide

  12. Example of Mutation
    mutation AddStar($input: AddStarInput!) {
    addStar(input: $input) {
    starrable {
    viewerHasStarred
    }
    }
    }

    View Slide

  13. Example of Subscription
    subscription sub {
    newMessage {
    ... newMessageFields
    }
    }
    fragment newMessageFields on Message {
    body
    sender
    }

    View Slide


  14. ここからようやくApollo Client
    の話

    View Slide

  15. DEMO (GitHub Client)

    View Slide

  16. 実装例にはReact
    を使います

    View Slide

  17. Creating a client and inject via a provider
    const networkInterface = createNetworkInterface({
    uri: 'https://api.github.com/graphql'
    });
    const middleWareInterface: MiddlewareInterface[] = [{
    applyMiddleware(req, next) {
    const headers = req.options.headers || {};
    AsyncStorage.getItem('token').then((token) => {
    headers.authorization = token ? `Bearer ${token}` : '';
    req.options.headers = headers;
    next();
    });
    }
    }];
    networkInterface.use(middleWareInterface);
    const client = new ApolloClient({ networkInterface });
    export default class App extends React.Component {
    render() {
    return (



    )
    }
    }

    View Slide

  18. Creating a client and inject via a provider
    作成したclient
    はprovider
    を通じて各コンポーネントで
    利用可能となる
    graphql()
    を用いてGraohQL Container
    を作成することが
    できる
    GraphQL Container
    は与えるprops
    の変化によるfetch

    自動で行う
    client
    を直接呼び出して使用することも可能( withApollo())

    View Slide

  19. Creating a GraphQL Container
    function ReleasesPage(props: Props & AppoloProps) {
    if (props.loading) {
    return Loading;
    }
    return (



    );
    }
    const withData: OperationComponent =
    graphql(RELEASES_QUERY, ({
    options: ({ owner, name }) => (
    {
    variables: { owner, name },
    notifyOnNetworkStatusChange: true
    }
    ),
    props: (props) => {
    const { loading, repository } = props.data;
    return {
    loading,
    repository,
    }
    }
    }));
    export default withData(ReleasesPage);

    View Slide

  20. GraphQL
    では大抵のリソースが
    1
    つのリクエストで取れる

    View Slide

  21. 件数が膨大なデータに対しても
    1
    つのリクエストで取得できる??

    View Slide

  22. そんなわけはない

    View Slide

  23. 件数が多ければ当然
    その分レスポンスは遅くなる

    View Slide

  24. つまりページネーション
    が必要

    View Slide

  25. data.fetchMore
    ページネーションを実現するためのAPI
    現在のfetching
    の状態をcache
    に残したまま、新しく取得し
    た結果をマージすることができる
    Relay.QL
    のcursor
    のパターンだけではなく、どのページネ
    ーションのパターンにでも使える

    View Slide

  26. Example of Pagination
    const withData: OperationComponent
    = graphql(REPOSITORY_QUERY, ({
    options: ({ queryString }) => (
    {
    variables: { queryString },
    notifyOnNetworkStatusChange: true
    }
    ),
    props: (props) => {
    const { loading, search, fetchMore } = props.data;
    return {
    loading,
    searchResult: { search },
    loadNextPage: (cursor) => {
    return fetchMore({
    variables: { cursor },
    updateQuery: (prev, data) => {
    const { search } = data.fetchMoreResult;
    search.edges = prev.search.edges.concat(search.edges);
    return { search };
    }
    })
    }
    }
    }
    }));

    View Slide

  27. Updating fetched data
    データの更新には前述の通りMutation
    を使う
    Mutation
    もQuery
    と同じように graphql()
    を用いて使用可能
    になる
    Mutaion
    の場合はQuery
    とは違い、props
    の変化に応じて実行
    されない

    View Slide

  28. Example of Mutaion
    function StarBadge(props: Props) {
    return (
    containerStyle={{ width: 160, height: 30, backgroundColor: '#fff', marginRight: 8 }}
    onPress={() =>
    rops.repository.viewerHasStarred ?
    props.removeStar(repository.id) :
    props.addStar(repository.id)}>
    {//...
    省略}

    );
    }
    const StarBadgeWithMutations = compose(
    graphql(ADD_STAR_MUTATION, {
    props: ({ ownProps, mutate }) => ({
    addStar: (id: string) =>
    mutate({ variables: { input: { starrableId: id } }, })
    })
    }),
    graphql(REMOVE_STAR_MUTATION, {
    props: ({ ownProps, mutate }) => ({
    removeStar: (id: string) =>
    mutate({ variables: { input: { starrableId: id } }, })
    })
    })
    )(StarBadge);

    View Slide

  29. Automatic store updates
    例ではMutation
    の発行のロジックしか書いていないにも関わ
    らず、store
    のデータも更新される
    mutation
    の結果で返ってきているid
    と同一のものがstore
    にあ
    る場合には、store
    も同時に更新される
    mutation RemoveStar($input: RemoveStarInput!) {
    removeStar(input: $input) {
    starrable {
    id
    stargazers {
    totalCount
    }
    viewerHasStarred
    }
    }
    }

    View Slide

  30. if you can't use automatic store updates...
    mutation
    のオプションを通じて手動でstore
    をupdate
    するこ
    とが可能
    refetchQueries: mutation
    の後に再度query
    を発行しデータを
    更新する
    update: mutation
    の後にstore
    のcache
    を直接いじってデータ
    を更新する
    updateQueries: deprecated

    View Slide

  31. あえてupdate
    を使ってcache
    を更新してみた
    const StarBadgeWithMutations = compose(
    graphql(ADD_STAR_MUTATION, {
    props: ({ ownProps, mutate }) => ({
    addStar: (id: string) =>
    mutate({ variables: { input: { starrableId: id } }, })
    })
    }),
    graphql(REMOVE_STAR_MUTATION, {
    props: ({ ownProps, mutate }) => {
    return {
    removeStar(id: string) {
    mutate({
    variables: { input: { starrableId: id } },
    update: (store, { data }) => {
    const queryOption = {
    query: RELEASES_QUERY,
    variables: { owner: ownProps.repository.owner.login, name:
    ownProps.repository.name }
    };
    const cache = store.readQuery(queryOption);
    cache.repository.viewerHasStarred = data.removeStar.starrable.viewerHasStarred;
    cache.repository.stargazers.totalCount -= 1;
    store.writeQuery(Object.assign({}, queryOption, { data: cache }));
    }
    })
    }
    }
    }
    })
    )(StarBadge);

    View Slide

  32. 大変なのでなるべく
    Automatic store updates
    しましょう!

    View Slide

  33. https://www.meetup.com/ja­JP/GraphQL­Tokyo/
    (多分)来月meetup
    するので、ご興味があればぜひ!

    View Slide

  34. Thank you for your
    attention!

    View Slide