RELAY
MODERN ARCHITECTURE

& WORKFLOW
@alexedev
Alex
Alexeev
@alexedev
Alex
Alexeev
React
@alexedev
Alex
Alexeev
React / GraphQL
@alexedev
Alex
Alexeev
React / GraphQL / Relay
👶 👶
👩
Fragment
Containers
👩
👶
👧
👶
👦
Query
Renderer
C1
C2
C3 C4
Fragment
Containers
👩
👶
👧
👶
👦
Query
Renderer
C1
C2
C3 C4
Fragment
Containers
👩
👶
👧
👶
Query
Renderer
props props
propsprops
👦 C1
C2
C3 C4
Component
+
data requirements
FRAGMENT
CONTAINER
👦
👦
TicketPrice.js
import { createFragmentContainer, graphql } from 'react-relay';
👦
TicketPrice.js
import { createFragmentContainer, graphql } from 'react-relay';
const TicketPrice = props =>
<span>{props.data.price.usd}</span>;
👦
TicketPrice.js
import { createFragmentContainer, graphql } from 'react-relay';
const TicketPrice = props =>
<span>{props.data.price.usd}</span>;
const query = graphql`
fragment TicketPriceFragment on FlightType {
price {
eur
usd
}
}
`;
👦
TicketPrice.js
import { createFragmentContainer, graphql } from 'react-relay';
const TicketPrice = props =>
<span>{props.data.price.usd}</span>;
const query = graphql`
fragment TicketPriceFragment on FlightType {
price {
eur
usd
}
}
`;
export default createFragmentContainer(TicketPrice, query);
👦
TicketPrice.js
Fragment
Containers
👩
👶
👧
👶
👦
Query
Renderer
C1
C2
C3 C4
Fragment
Containers
👩
👶
👧
👶
Query
Renderer
props props
propsprops
👦 C1
C2
C3 C4
QUERY
RENDERER
👩
Root of Relay tree
Distributes data to
Gathers data requirements from 👦
Sends the query to server
👦
import { QueryRenderer, graphql } from 'react-relay';
👩
Flights.js
import { QueryRenderer, graphql } from 'react-relay';
import TicketPrice from './TicketPrice';
👩
Flights.js
import { QueryRenderer, graphql } from 'react-relay';
import TicketPrice from './TicketPrice';
const query = graphql`
query FlightsQuery($city: String) {
flights(from: $city) {
id
destination
}
}
`;
👩
Flights.js
import { QueryRenderer, graphql } from 'react-relay';
import TicketPrice from './TicketPrice';
const query = graphql`
query FlightsQuery($city: String) {
flights(from: $city) {
id
destination
}
}
`;
👩
Flights.js
import { QueryRenderer, graphql } from 'react-relay';
import TicketPrice from './TicketPrice';
const query = graphql`
query FlightsQuery($city: String) {
flights(from: $city) {
id
destination
...TicketPriceFragment
}
}
`;
👩
Flights.js
import { QueryRenderer, graphql } from 'react-relay';
import TicketPrice from './TicketPrice';
const Flights = () => (
<QueryRenderer
environment={environment}
query={query}
variables={{ from: 'Barcelona' }}
render={({ error, props }) =>
props.flights.map(flight => (
<div key={flight.id}>
To: {flight.destination}
<TicketPrice data={flight} />
</div>
))
}
/>
);
👩
Flights.js
const query = graphql`
query FlightsQuery($city: String) {
flights(from: $city) {
id
destination
...TicketPriceFragment
}
}
`;
REFETCH
CONTAINER
♻
PAGINATION
CONTAINER
📑
UPDATE
CREATE
DELETE
MUTATIONS
💉
KEEP
CALM
AND
MUTATIONS
import { commitMutation, graphql } from 'react-relay';
import { commitMutation, graphql } from 'react-relay';
const mutation = graphql`
mutation ChangePriceMutation($id: ID!, $priceUsd: Int) {
changePrice(id: $id, price: $priceUsd) {
flight {
id
price {
usd
eur
}
}
}
}
`;
MUTATIONS
MUTATIONS
import { commitMutation, graphql } from 'react-relay';
const mutation = graphql`
mutation ChangePriceMutation($id: ID!, $priceUsd: Int) {
changePrice(id: $id, price: $priceUsd) {
flight {
id
price {
usd
eur
}
}
}
}
`;
function sellItem(environment, id, priceUsd) {
const variables = {
id,
priceUsd,
};
commitMutation(
environment,
{
mutation,
variables,
// configs,
// updater,
onCompleted: (res, err) => console.log(res),
onError: err => console.error(err),
},
);
}
MUTATIONS
function sellItem(environment, id, priceUsd) {
const variables = {
id,
priceUsd,
};
commitMutation(
environment,
{
mutation,
variables,
// configs,
// updater,
onCompleted: (res, err) => console.log(res),
onError: err => console.error(err),
},
);
}
MUTATIONS
function sellItem(environment, id, priceUsd) {
const variables = {
id,
priceUsd,
};
commitMutation(
environment,
{
mutation,
variables,
// configs,
// updater,
onCompleted: (res, err) => console.log(res),
onError: err => console.error(err),
},
);
}
MUTATIONS
function sellItem(environment, id, priceUsd) {
const variables = {
id,
priceUsd,
};
commitMutation(
environment,
{
mutation,
variables,
// configs,
// updater,
onCompleted: (res, err) => console.log(res),
onError: err => console.error(err),
},
);
}
MUTATIONS
function sellItem(environment, id, priceUsd) {
const variables = {
id,
priceUsd,
};
commitMutation(
environment,
{
mutation,
variables,
// configs,
// updater,
onCompleted: (res, err) => console.log(res),
onError: err => console.error(err),
},
);
}
MUTATIONS
MUTATION
CONFIGS
NODE_DELETE

RANGE_ADD

RANGE_DELETE
const configs = [{
type: 'NODE_DELETE',
deletedIDFieldName: 'id',
}]
🚀
function sellItem(environment, id, priceUsd) {
const variables = {
id,
priceUsd,
};
commitMutation(
environment,
{
mutation,
variables,
// configs,
// updater,
onCompleted: (res, err) => console.log(res),
onError: err => console.error(err),
},
);
}
MUTATIONS
MUTATIONS
import { commitMutation, graphql } from 'react-relay';
const mutation = graphql`
mutation ChangePriceMutation($id: ID!, $priceUsd: Int) {
changePrice(id: $id, price: $priceUsd) {
flight {
id
price {
usd
eur
}
}
}
}
`;
UPDATE STORE
BY HAND 🛠
function sellItem() {
commitMutation({
//…
updater: store => {
const resItem = store
.getRootField('changePrice')
.getLinkedRecord('flight');
const newPrice = resItem.getValue('price');
const item = store.get(id);
item.setValue(newPrice, 'price');
},
});
}
UPDATE STORE
BY HAND 🛠
function sellItem() {
commitMutation({
//…
updater: store => {
const resItem = store
.getRootField('changePrice')
.getLinkedRecord('flight');
const newPrice = resItem.getValue('price');
const item = store.get(id);
item.setValue(newPrice, 'price');
},
});
}
UPDATE STORE
BY HAND 🛠
function sellItem() {
commitMutation({
//…
updater: store => {
const resItem = store
.getRootField('changePrice')
.getLinkedRecord('flight');
const newPrice = resItem.getValue('price');
const item = store.get(id);
item.setValue(newPrice, 'price');
},
});
}
UPDATE STORE
BY HAND 🛠
function sellItem() {
commitMutation({
//…
updater: store => {
const resItem = store
.getRootField('changePrice')
.getLinkedRecord('flight');
const newPrice = resItem.getValue('price');
const item = store.get(id);
item.setValue(newPrice, 'price');
},
});
}
UPDATE STORE
BY HAND 🛠
function sellItem() {
commitMutation({
//…
updater: store => {
const resItem = store
.getRootField('changePrice')
.getLinkedRecord('flight');
const newPrice = resItem.getValue('price');
const item = store.get(id);
item.setValue(newPrice, 'price');
},
});
}
UPDATE STORE
BY HAND 🛠
function sellItem() {
commitMutation({
//…
updater: store => {
const resItem = store
.getRootField('changePrice')
.getLinkedRecord('flight');
const newPrice = resItem.getValue('price');
const item = store.get(id);
item.setValue(newPrice, 'price');
},
});
}
UPDATE STORE
BY HAND 🛠
function sellItem() {
commitMutation({
//…
updater: store => {
const resItem = store
.getRootField('changePrice')
.getLinkedRecord('flight');
const newPrice = resItem.getValue('price');
const item = store.get(id);
item.setValue(newPrice, 'price');
},
});
}
SUBSCRIPTIONS
Allow to implement real time updates: 

notifications, messages…
	1.	Implement on server side first 

via WebSockets (AWS AppSync for scalability)

	

2.	 Use Relay API similar to mutations (requestSubscription)

📨
CHROME EXTENSION
CHROME EXTENSION
Q & A
🤓
SEE YA!
🙂

Relay Modern: architecture and workflow