Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
GraphqlとReact
Search
Taketoshi Aono(青野健利 a.k.a brn)
June 07, 2017
Programming
0
98
GraphqlとReact
GraphqlとReactを組み合わせた場合のフレームワークの良し悪しとか紹介とか
Taketoshi Aono(青野健利 a.k.a brn)
June 07, 2017
Tweet
Share
More Decks by Taketoshi Aono(青野健利 a.k.a brn)
See All by Taketoshi Aono(青野健利 a.k.a brn)
document.write再考
brn
5
2.5k
Parsing Javascript
brn
12
6.9k
JSON & Object Tips
brn
1
280
CA 1Day Youth Bootcamp for Frontend LT
brn
0
660
Modern TypeScript
brn
2
680
javascript - behind the scene
brn
3
590
tc39 proposals
brn
0
640
プロダクト開発とTypeScript
brn
8
2.7k
React-Springでリッチなアニメーション
brn
1
490
Other Decks in Programming
See All in Programming
WasmOS: Wasmを実行する自作Microkernel
riru
0
370
ファイル先頭の use の意味、説明できますか? 〜PHP の namespace と autoloading の関係を正しく理解しよう〜 / namespace and autoloading in php
okashoi
2
470
ここ1~2年くらいで 使えるようになった(主要ブラウザーの最新版 がすべて対応した ) ウェブの新機能について ランダムに喋る!
myzkyy
9
6.4k
WinUI 3デモ - "CommunityToolkit.Mvvm"NuGetパッケージ編
andrewkeepcoding
0
130
導入から5年が経って見えた Datadog APM 運用の課題
bgpat
2
540
上手な探索的テストとその上達方法について
matsu802
4
650
Honoとhtmx
yusukebe
6
1.2k
GitHub Copilot Tips and Tricks
yuichielectric
26
7.4k
SwiftUI, Jetpack Composeの導入で変化した「家族アルバム みてね」のアプリ開発体験
hicka04
6
390
App Router への移行は「改善」となり得るのか?/ Can migration to App Router be an improvement
takefumiyoshii
1
120
BuefyのMaintainerを引き継いだ件
kikuomax
0
510
RubyVM を PHP で実装する 〜Hello World を出力するまで〜
memory1994
PRO
1
490
Featured
See All Featured
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
28
5.9k
Imperfection Machines: The Place of Print at Facebook
scottboms
257
12k
Designing for Performance
lara
601
67k
Gamification - CAS2011
davidbonilla
76
4.5k
Product Roadmaps are Hard
iamctodd
43
9.6k
Designing Experiences People Love
moore
135
23k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
18
1.7k
Typedesign – Prime Four
hannesfritz
36
2k
Building Effective Engineering Teams - LeadDev
addyosmani
25
1.8k
jQuery: Nuts, Bolts and Bling
dougneiner
57
7.1k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
39
4.3k
Build The Right Thing And Hit Your Dates
maggiecrowley
23
1.9k
Transcript
Graphql with Reactjs
名前: @brn (青野健利) 職業: フロントエンドエンジニア・ネイティブエンジニア 会社: Cyberagent アドテクスタジオ RightSegment・AI Messenger
ブログ: http://abcdef.gets.b6n.ch/
Why graphql? フロントエンド主導でAPIの設計ができる。 より複雑化していくSPAの設計に向いている。
Why React? Viewを宣言的に書ける。 状態をViewから切り離すことができる。 これらをそこそこの速度で実現している。
React+Graphql Reactでgraphqlを使うなら、できるだけ親和性が高いライブラリを選びたい。
Relay Facebook製のライブラリで、Reactを組み合わせる前提で作られた。 Facebookでは実際に使用されている。
Relay インストール npm install react-relay --save
Relay import React from 'react'; import Relay from 'react-relay'; class
App extends React.Component { render() { return ( <ul> {this.props.todos.todos.edges.map(edge => <li key={edge.node.id}>{edge.node.name} (ID: {edge.node.id})</li>)} </ul> ); } } Component
Relay export default Relay.createContainer(App, { fragments: { todos: () =>
Relay.QL` fragment on Todos { todos(first: 1) { edges { node { id value }, }, }, } `, }, }); Container
Relay Relay Container Component React Component Server Parent Component Relay
Store Props Read Query Response Response
Relay import Relay from 'react-relay'; export default class extends Relay.Route
{ static queries = { viewer: () => Relay.QL` query { todos } `, }; static routeName = 'AppHomeRoute'; } Route
Relay import 'babel-polyfill'; import App from './components/App'; import AppHomeRoute from
'./routes/AppHomeRoute'; import React from 'react'; import ReactDOM from 'react-dom'; import Relay from 'react-relay'; ReactDOM.render( <Relay.Renderer environment={Relay.Store} Container={App} queryConfig={new AppHomeRoute()} />, document.getElementById('root') ); App
Relay これで完成!と言いたいところですが… RelayはBabelを前提に作られており、 Relay.QL`<query>` の部分はランタイムでは実行できません。 Babelによってコンパイルされることで関数になり、実行できるようになります。 なので、早速 npm install babel-relay-plugin
でインストールします。
Relay 今度こそ! Error : Invalid introspection data supplied to `getBabelRelayPlugin()`.
The resulting schema is not an object with a `__schema` property or a schema IDL language. while parsing file…
Relay どうやらSchema.jsonと言うものが必要なようなのですが、 作り方も何なのかもドキュメントがなく、ただ、 relay-starter-kitというgitリポジトリにあるとだけ書いてあります…
Relay
Relay schema.jsonの正体はGraphQLの型定義のメタデータで、 introspectionQueryという機能を利用し自動生成する。 relay-starter-kitのscriptsディレクトリに詳細があるので、そこをみるとよろし。 私はもうRelayはやめます!
Relay ここが駄目だよRelayちゃん とにかくAPIが複雑。すぐ使えるようなものではない上に Documentがexampleベースなのだが、そのexampleも複雑なので、 読む気がしない。 Babel前提なのはいいが、schema.json作れとか面倒すぎる。 Relay.Routeとか意味がわかりづらすぎ。 Reduxとも組み合わせづらい relay-modernである程度はよくなりそうだが…
React-apollo そこで、react-apolloです
React-apollo React-apolloとは React環境(React-native等)ならどこでも動くGraphQLクライアント ビルド設定等不要で、jsランタイムさえあれば動きます。 基本的にかなりシンプルで使いやすいです。
React-apollo インストール npm install react-apollo --save
React-apollo import { ApolloClient, createNetworkInterface } from 'react- apollo'; export
const client = new ApolloClient({ networkInterface: createNetworkInterface({ uri: 'http://localhost:3000/graphql' }) }); Client
React-apollo import { gql, graphql } from 'react-apollo'; import React
from 'react'; const QUERY = gql` query TodoAppQuery($id: ID) { todos(id: $id) { id text } }`; @graphql(QUERY, { options(props) { return {id: props.id} } }) export default class TodoApp extends React.Component { render() { const {fetchMore, refetch, todos = []} = this.props.data; return ( <div> <button onClick={() => refetch()}>Refresh</button> <ul>{todos.map(todo => <li key={todo.id}>{todo.text}</li>)}</ul> </div> ); } } Component
React-apollo import { ApolloProvider } from 'react-apollo'; import ReactDOM from
'react-dom'; import React from 'react'; import {client} from './client'; import TodoApp from './todo'; ReactDOM.render(( <ApolloProvider client={client}> <TodoApp id={null}/> </ApolloProvider> ), document.querySelector('#app')); App
React-apollo 後はBabel等でjsxをコンパイルすれば動きます。 特に面倒な設定は無いはず!
React-apollo Client Apolloがサーバにアクセスする際のエンドポイントを指定します。 ここにはMiddlewareも指定できて、リクエスト・レスポンスの編集も可能です。
React-apollo import ApolloClient, { createNetworkInterface } from 'apollo-client’; const networkInterface
= createNetworkInterface({ uri: '/graphql' }); networkInterface.use([{ applyMiddleware(req, next) { if (!req.options.headers) { req.options.headers = {}; // Create the header object if needed. } req.options.headers['authorization'] = localStorage.getItem('token') ? localStorage.getItem('token') : null; next(); } }]); const client = new ApolloClient({ networkInterface, }); App
React-apollo Component apolloでReactComponentをラップすることで、 GraphQLのリクエストを行える様にする。 ここはRelayと同じだが、Relayと違ってES Decoratorを利用する事ができるのと、 コンポーネント側に便利なPropsが提供されている。
React-apollo data: { loading: false, error: null, variables: { id:
'asdf' }, refetch() { ... }, fetchMore() { ... }, startPolling() { ... }, stopPolling() { ... }, // ... more methods } this.props.data配下に便利なメソッド、プロパティが定義される
React-apollo ApolloProvider 最後にApolloProviderでReactComponentTreeにGraphQLリクエスト機能を渡す。 ここで、ReduxのStoreと連携することも可能
React-apollo+Redux import { gql, graphql } from 'react-apollo'; import {connect}
from 'react-redux'; import React from 'react'; import {addTodo, getTodos} from './action'; @connect((state) => state, (dispatch) => ({ addTodo(todo) {return dispatch(addTodo(todo))}, getTodos(todo) {return dispatch(getTodos(todo))} })) @graphql(ADD_QUERY, { props({ownProps, mutate}) { return { addTodo(text) { mutate({variables: {text}}).then(t => ownProps.addTodo(t.data.addTodo)); } } } }) @graphql(QUERY, { options(props) { return {id: props.id}; }, skip: props => typeof window === 'object' }) export default class TodoApp extends React.Component { constructor(p, c) { super(p, c); } render() { const {todosRoot: {todos = []}, addTodo} = this.props; return ( <div> <div> <input type="text" onChange={e => this._handleInput(e)}/> <button onClick={() => addTodo(this._value)}>add</button> </div> <button onClick={() => this.props.apollo.refetch()}>Refresh</button> <ul>{todos.map(todo => <li key={todo.id}>{todo.text}</li>))}</ul> </div> ); } } Component
React-apollo+Redux import { createStore, combineReducers, applyMiddleware, compose } from 'redux’;
import {ApolloClient} from 'react-apollo'; import todoReducer from './reducer'; export default (client) => { return createStore( combineReducers({ todosRoot: todoReducer, apollo: client.reducer() }), typeof window === 'object'? window.__APOLLO_STATE__: {}, // initial state compose( applyMiddleware(client.middleware()), (typeof window === 'object' && typeof indow.__REDUX_DEVTOOLS_EXTENSION__ ! == 'undefined') ? window.__REDUX_DEVTOOLS_EXTENSION__() : f => f ) ); }; Store
React-apollo SSR React-apolloはServerSideRenderingにも対応していて、 簡単な設定でSSRできます!
React-apollo SSR import 'isomorphic-fetch'; import fs from 'fs'; import express
from 'express'; import ejs from 'ejs'; import React from 'react'; import TodoApp from '../client/src/todo.jsx'; import {client} from '../client/src/client'; import {renderToString} from 'react-dom/server'; import {ApolloProvider, getDataFromTree} from 'react-apollo'; const app = express(); const template = fs.readFileSync('./index.html', 'utf8'); const tree = <ApolloProvider client={client}><TodoApp id={1}/></ ApolloProvider>; app.get('/', (req, res) => { getDataFromTree(tree).then(v => { res.end(ejs.render(template, {dom: renderToString(tree)})); }); }); app.listen(3000);
React-apollo SSR SSRで重要な点 • isomorphic-fetchのインストール • getDataFromTreeで実際にリクエストを行う • 初期状態をhtml上にjsonで渡す。
React-apollo SSR まとめ react-apolloとnodejsでisomorphicなGraphQL生活を!