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
490
CA 1Day Youth Bootcamp for Frontend LT
brn
0
950
Modern TypeScript
brn
2
810
javascript - behind the scene
brn
3
740
tc39 proposals
brn
0
880
プロダクト開発とTypeScript
brn
8
2.9k
React-Springでリッチなアニメーション
brn
1
700
Other Decks in Programming
See All in Programming
The Niche of CDK Grant オブジェクトって何者?/the-niche-of-cdk-what-isgrant-object
hassaku63
1
500
Webの外へ飛び出せ NativePHPが切り拓くPHPの未来
takuyakatsusa
2
580
AI駆動のマルチエージェントによる業務フロー自動化の設計と実践
h_okkah
0
210
明示と暗黙 ー PHPとGoの インターフェイスの違いを知る
shimabox
2
590
Git Sync を超える!OSS で実現する CDK Pull 型デプロイ / Deploying CDK with PipeCD in Pull-style
tkikuc
4
240
AIともっと楽するE2Eテスト
myohei
8
2.9k
Goで作る、開発・CI環境
sin392
0
260
GitHub Copilot and GitHub Codespaces Hands-on
ymd65536
2
150
ソフトウェア品質を数字で捉える技術。事業成長を支えるシステム品質の マネジメント
takuya542
2
14k
Porting a visionOS App to Android XR
akkeylab
0
660
20250628_非エンジニアがバイブコーディングしてみた
ponponmikankan
0
710
The Evolution of Enterprise Java with Jakarta EE 11 and Beyond
ivargrimstad
0
180
Featured
See All Featured
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
15
1.6k
Automating Front-end Workflow
addyosmani
1370
200k
Large-scale JavaScript Application Architecture
addyosmani
512
110k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
29
1.8k
KATA
mclloyd
30
14k
Unsuck your backbone
ammeep
671
58k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
7
740
Facilitating Awesome Meetings
lara
54
6.5k
Side Projects
sachag
455
42k
Embracing the Ebb and Flow
colly
86
4.7k
Building a Modern Day E-commerce SEO Strategy
aleyda
42
7.4k
Documentation Writing (for coders)
carmenintech
72
4.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生活を!