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
120
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
GraphqlとReact
GraphqlとReactを組み合わせた場合のフレームワークの良し悪しとか紹介とか
Taketoshi Aono(青野健利 a.k.a brn)
June 07, 2017
More Decks by Taketoshi Aono(青野健利 a.k.a brn)
See All by Taketoshi Aono(青野健利 a.k.a brn)
document.write再考
brn
6
3.2k
Parsing Javascript
brn
14
9.6k
JSON & Object Tips
brn
1
580
CA 1Day Youth Bootcamp for Frontend LT
brn
0
1.1k
Modern TypeScript
brn
2
880
javascript - behind the scene
brn
3
820
tc39 proposals
brn
0
1k
プロダクト開発とTypeScript
brn
7
3k
React-Springでリッチなアニメーション
brn
1
770
Other Decks in Programming
See All in Programming
Laravelで学ぶ Webアプリケーションチューニング入門/web_application_tuning_101
hanhan1978
4
1.6k
作るコストが小さくなった時代 幸せに働くために改めて考えたいこと 〜エンジニアとして価値を出し続けるために注視している二分野〜
yuppeeng
0
180
FDEが実現するAI駆動経営の現在地
gonta
2
250
全PRの83%がAIレビューだけでマージできるようになった開発組織はその後どうなったか
athug
1
1.3k
<title><a id="</title>君はこのHTMLをパースできるか"></a></title> #雑LT_study
pizzacat83
0
130
Prismを使った型安全な暗号化_関数型まつり2026
_fhhmm
0
170
5分で問診!Composer セキュリティ健康診断
codmoninc
0
840
人間の目はかわらない、だからJPEGは30年もつ
yuzneri
12
18k
改善しないと、タスクが回らない。 “てんこ盛りポジション” を引き継いだ情シスの、入社3ヶ月の業務改善録
krm963
0
250
AWS DevOps AgentのAzure接続機能を検証して見えた活用法/Use Cases Verified for the AWS DevOps Agent's Azure Connectivity Feature
masakiokuda
1
210
AI時代、エンジニアはどう育つのか -未経験エンジニアの成長を間近で見て考えたこと-
thasu0123
0
210
なぜ関数型プログラミングで「型」と「証明」が語られるのか #fp_matsuri
kajitack
3
1.1k
Featured
See All Featured
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
We Have a Design System, Now What?
morganepeng
55
8.2k
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2.2k
Statistics for Hackers
jakevdp
799
230k
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
400
Self-Hosted WebAssembly Runtime for Runtime-Neutral Checkpoint/Restore in Edge–Cloud Continuum
chikuwait
0
680
Game over? The fight for quality and originality in the time of robots
wayneb77
1
240
Max Prin - Stacking Signals: How International SEO Comes Together (And Falls Apart)
techseoconnect
PRO
0
330
Git: the NoSQL Database
bkeepers
PRO
432
67k
Reflections from 52 weeks, 52 projects
jeffersonlam
356
21k
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
610
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
250
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生活を!