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
110
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
6
3k
Parsing Javascript
brn
14
9.3k
JSON & Object Tips
brn
1
510
CA 1Day Youth Bootcamp for Frontend LT
brn
0
970
Modern TypeScript
brn
2
820
javascript - behind the scene
brn
3
750
tc39 proposals
brn
0
890
プロダクト開発とTypeScript
brn
8
2.9k
React-Springでリッチなアニメーション
brn
1
720
Other Decks in Programming
See All in Programming
Cache Me If You Can
ryunen344
2
4k
複雑なドメインに挑む.pdf
yukisakai1225
5
1.2k
Improving my own Ruby thereafter
sisshiki1969
1
160
testingを眺める
matumoto
1
140
プロポーザル駆動学習 / Proposal-Driven Learning
mackey0225
2
1.3k
AWS発のAIエディタKiroを使ってみた
iriikeita
1
190
print("Hello, World")
eddie
2
530
Performance for Conversion! 分散トレーシングでボトルネックを 特定せよ
inetand
0
3.4k
スケールする組織の実現に向けた インナーソース育成術 - ISGT2025
teamlab
PRO
2
170
Amazon RDS 向けに提供されている MCP Server と仕組みを調べてみた/jawsug-okayama-2025-aurora-mcp
takahashiikki
1
120
ユーザーも開発者も悩ませない TV アプリ開発 ~Compose の内部実装から学ぶフォーカス制御~
taked137
0
190
@Environment(\.keyPath)那么好我不允许你们不知道! / atEnvironment keyPath is so good and you should know it!
lovee
0
130
Featured
See All Featured
Being A Developer After 40
akosma
90
590k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
358
30k
Making Projects Easy
brettharned
117
6.4k
Rebuilding a faster, lazier Slack
samanthasiow
83
9.2k
What's in a price? How to price your products and services
michaelherold
246
12k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
Mobile First: as difficult as doing things right
swwweet
224
9.9k
It's Worth the Effort
3n
187
28k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
31
2.2k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
34
6k
Speed Design
sergeychernyshev
32
1.1k
Intergalactic Javascript Robots from Outer Space
tanoku
272
27k
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生活を!