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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  5. Fundations of
    GraphQL

    View full-size slide

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

    View full-size slide

  7. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  10. 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 full-size slide

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

    View full-size slide

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

    View full-size slide


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

    View full-size slide

  14. DEMO (GitHub Client)

    View full-size slide

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

    View full-size slide

  16. 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 full-size slide

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

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

    View full-size slide

  18. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  21. そんなわけはない

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  25. 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 full-size slide

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

    View full-size slide

  27. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  30. あえて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 full-size slide

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

    View full-size slide

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

    View full-size slide

  33. Thank you for your
    attention!

    View full-size slide