relayのtodo exampleを見てみた。
以下が、importされていた。ということは以下の要素を読み解けばrelayが何かわかるはずだ。
import {
createFragmentContainer,
QueryRenderer,
graphql,
} from 'react-relay';
import {
Environment,
Network,
RecordSource,
Store,
} from 'relay-runtime';
以上のものを解説していく。 (一時間を予定)
1.graphql
以下のようにgraphqlのテンプレートを定義する。
引数など、細かな使い方はあるが、基本的にはクエリを定義するだけ。
import {graphql} from 'react-relay';
graphql`
query MyQuery {
viewer {
id
}
}
`;
2.Environment
環境変数を定義する。 引数は NetWork, RecordSource, Store
storeとnetworkは必須。別々の環境変数を定義して、二つのEnvironmentを作成することも可能。
graphqlサーバーと通信するための設定を全てここで行う。
const {
Environment,
Network,
RecordSource,
Store,
} = require('relay-runtime');
const source = new RecordSource();
const store = new Store(source);
const network = Network.create(/*...*/); // see note below
const handlerProvider = null;
const environment = new Environment({
handlerProvider, // Can omit.
network,
store,
});
3.Network
graphqlサーバと通信するための設定をここに書く。書いたNetWorkはEnvironmentに食わせる
import {
Environment,
Network,
RecordSource,
Store,
} from 'relay-runtime';
// Define a function that fetches the results of an operation (query/mutation/etc)
// and returns its results as a Promise:
function fetchQuery(
operation,
variables,
cacheConfig,
uploadables,
) {
return fetch('/graphql', {
method: 'POST',
headers: {
// Add authentication and other headers here
'content-type': 'application/json'
},
body: JSON.stringify({
query: operation.text, // GraphQL text from input
variables,
}),
}).then(response => {
return response.json();
});
}
// Create a network layer from the fetch function
const network = Network.create(fetchQuery);
const store = new Store(new RecordSource())
const environment = new Environment({
network,
store
// ... other options
});
export default environment;
4.queryRenderer
queryRendererはrelayコンポーネントツリーの頂上にあるコンポーネントです。
queryとfetchを受け取り、結果をpropsとしてレンダリングします。
reduxのproviderのような役割のものかな?
import React from 'react';
import { QueryRenderer, graphql } from 'react-relay';
class Example extends React.Component {
render() {
return (
<QueryRenderer
environment={environment}
query={graphql`
query ExampleQuery($pageID: ID!) {
page(id: $pageID) {
name
}
}
`}
variables={{
pageID: '110798995619330',
}}
render={({error, props}) => {
if (error) {
return <div>{error.message}</div>;
} else if (props) {
return <div>{props.page.name} is great!</div>;
}
return <div>Loading</div>;
}}
/>
);
}
}
5.FragmentContainer
FragmentContainerはHOCです。このcontainerはdataをfetchするわけではありません。しかし、ネストされたレンダリングを宣言することでレンダリングします。
createFragmentContainer(
component: ReactComponentClass,
fragmentSpec: GraphQLTaggedNode | {[string]: GraphQLTaggedNode},
): ReactComponentClass;
以上の例だと何をしているかわからないですね。以下でもっと実践的な例を見てみます。
TodoItemコンポーネントがあると仮定します。このコンポーネントはtextと、todoが完了したかのstatusを持ちます。
// TodoItem.js
class TodoItem extends React.Component {
render() {
// Expects the `item` prop to have the following shape:
// {
// item: {
// text,
// isComplete
// }
// }
const item = this.props.item;
return (
<View>
<Checkbox checked={item.isComplete} />
<Text>{item.text}</Text>
</View>
);
}
}
relayではデータ依存はGraphqlを使って解決します。依存は以下のように解決されます。
graphql`
# This fragment only applies to objects of type 'Todo'.
fragment TodoItem_item on Todo {
text
isComplete
}
`
import {createFragmentContainer, graphql} from 'react-relay';
class TodoItem extends React.Component {/* as above */}
// Export a *new* React component that wraps the original `<TodoItem>`.
export default createFragmentContainer(TodoItem, {
// For each of the props that depend on server data, we define a corresponding
// key in this object. Here, the component expects server data to populate the
// `item` prop, so we'll specify the fragment from above at the `item` key.
item: graphql`
fragment TodoItem_item on Todo {
text
isComplete
}
`,
});
以上のようにreactのcomponentとgraphqlが与えられた時、createFragmentContainerでcomponentが要求するデータを指定できます。
要するにcreateFragmentContainerはcomponentのclass名とgraphqlを引数にとり、通信機能をもつ新しい
containerを作ります。(reduxのconnectとページ固有のsagaが一緒になった感じ?)
export default createFragmentContainer(
TodoItem,
graphql`
fragment TodoItem_item on Todo {
text
isComplete
}
`,
);
createFragmentContainerの中でfetchしたデータは以下のようにpropsで取り出せます。
class TodoItem extends React.Component {
render() {
const item = this.props.data;
// ...
}
}
export default createFragmentContainer(
TodoItem,
graphql`
fragment TodoItem on Todo {
text
isComplete
}
`,
);
createRefetchContainer
createFragmentsとほぼ同じ。
createFragmentsはページがレンダリングされるときにfetchする。
ただこちらはrefetchができる。
createFragmentsとほぼ同じ
リストなどで、データを先に読み込んでおきたい時に使う。
Relay Store
reduxのstoreのようなもの? なんか3種類あるみたい。
RecordSourceSelectorProxy
RecordProxy recordをmutateするインターフェース
ConnectionHandler relationを扱う感じ?
RecordSourceSelectorProxy
RecordSourceSelectorProxyは以下のupdater functionを引数にとるstoreのタイプ?
interface RecordSourceSelectorProxy {
create(dataID: string, typeName: string): RecordProxy;
delete(dataID: string): void;
get(dataID: string): ?RecordProxy;
getRoot(): RecordProxy;
getRootField(fieldName: string): ?RecordProxy;
getPluralRootField(fieldName: string): ?Array;
}
以下は例
const record = store.create(dataID, 'Todo');
store.delete(dataID);
const record = store.get(dataID);
const root = store.getRoot();
それではtodoアプリの解説を書いていく。
まず、App.js
// react-relay
QueryRenderer,
graphql,
// relay-runtime
Environment,
Network,
RecordSource,
Store,