Apollo graphql client представляет удобный лаконичный спсоб работы с данными в приложениях react. В большинстве случаев все то, что мы привыкли делать с помощью redux, гораздо проще сделать при помощи Apollo graphql client. То, о чем я хотел бы рассказать в этой статье — это что связка react + apollo client + graphql существенно (на порядок) упрощает разработку приложений react.
Для работы мы будем использовать тестовый сервер graphql по адресу. graphql из коробки предлагает, кроме всего прочего, консоль для выполнения запросов, и в ней же интегрированную документацию. Предлагаю раскрыть эту панель. В левой панели можно вводить запрос, в правой будет выводиться ответ. Попробуйте ввести самый простой запрос:
allUsers это имя запроса, а внутри фигурных скобок — имена полей которые будут возвращены. Более сложные запросы могут содержать список параметров и содержать поля вложенных объектов:
В данном случае параметры orderBy и limit не следует воспринимать как в SQL. Это просто имена параметров которые сами по себе без реализации не делают сортировку или ограничение выборки.
Другие параметры и выходные поля этих запросов можно посмотреть из интерфейса консоли.
В качестве основы для приложения возьмем react-create-app. Дополнительно установим apollo-client и react-router-dom:
Изменим основной компонент приложения App.js:
AploolProvider и ApolloClient — это все что нужно для того чтобы во всех компонентах можно было использовать graphql.
Проще всего передать данные в компонент с использованием тэга Query. Давайте выведем в компоненте список пользователей (сам запрос мы уже опробовали в консоли раньше):
Для добавления или изменения состояния на сервере используются Mutation:
Когда я знакомился с примерами компонетов react с использованием apollo graphql client я чувствовал себя не совсем уютно. А все дело в том что разработчик веб-приложения мыслит скорее императивно, чем декларативно. Нам хочется действия. «Установить фильтр», «сохранить запись» и т.п. И когда мы переходим к тому что нам нет необходимости думать о том как данные грузятся с сервера мы вместо того чтобы принять такой подход и использоватьего преимущества задумываемся над тем как мы будем контролировать store.
Дополняю текст еще одним примером, в котором в запросе graphql используется параметр принятый от охватывающего компонента (роута).
Исходный код.
Также Apollo client работает и в условиях серверного рендеринга и универсальных приложений. Предзагрузка данных для серверного рендеринга (включая и все вложенные компоненты) реализована «из коробки». Парадокс в том что соединив несколько технологий которые часто критикуют за усложненность получили в результате существенное упрощение приложения.
apapacy@gmail.com
11 мая 2018 года.
Для работы мы будем использовать тестовый сервер graphql по адресу. graphql из коробки предлагает, кроме всего прочего, консоль для выполнения запросов, и в ней же интегрированную документацию. Предлагаю раскрыть эту панель. В левой панели можно вводить запрос, в правой будет выводиться ответ. Попробуйте ввести самый простой запрос:
query {
allUsers {
id
name
}
}
allUsers это имя запроса, а внутри фигурных скобок — имена полей которые будут возвращены. Более сложные запросы могут содержать список параметров и содержать поля вложенных объектов:
query {
allPosts(orderBy: updatedAt_DESC, limit: 7){
id
title
user {
id
name
}
}
}
В данном случае параметры orderBy и limit не следует воспринимать как в SQL. Это просто имена параметров которые сами по себе без реализации не делают сортировку или ограничение выборки.
Другие параметры и выходные поля этих запросов можно посмотреть из интерфейса консоли.
В качестве основы для приложения возьмем react-create-app. Дополнительно установим apollo-client и react-router-dom:
npm install apollo-boost react-apollo graphql-tag graphql --save
npm install react-router-dom --save
Изменим основной компонент приложения App.js:
import React from 'react';
import { Route, Switch, BrowserRouter } from 'react-router-dom';
import { ApolloProvider } from 'react-apollo';
import ApolloClient from 'apollo-boost';
import Layout from './Layout';
import AllUsers from './AllUsers';
import TopPosts from './TopPosts';
import NewPost from './NewPost';
const client = new ApolloClient({
uri: 'https://api.graph.cool/simple/v1/ciyz901en4j590185wkmexyex',
});
const App = () => (
<ApolloProvider client={client}>
<BrowserRouter>
<Layout>
<Switch>
<Route exact path='/' component={ AllUsers } />
<Route exact path='/posts' component={ TopPosts } />
<Route exact path='/user/:userId' component={ NewPost } />
</Switch>
</Layout>
</BrowserRouter>
</ApolloProvider>
);
export default App;
AploolProvider и ApolloClient — это все что нужно для того чтобы во всех компонентах можно было использовать graphql.
Проще всего передать данные в компонент с использованием тэга Query. Давайте выведем в компоненте список пользователей (сам запрос мы уже опробовали в консоли раньше):
import React from 'react';
import { Link } from 'react-router-dom'
import { Query } from "react-apollo";
import gql from "graphql-tag";
import TopPosts from './TopPosts';
const AllUsers = () => (
<Query
query={gql`
query {
allUsers {
id
name
}
}
`}
>
{({ loading, error, data }) => {
for (let key in arguments[0])
console.log(key, arguments[0][key]);
console.log('data', data)
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
return (
<ul key='allUsers'>
{data.allUsers.map(({ id, name }) => (
<li key={id}><Link to={`/user/${id}`}>{name ? name : 'incognoito'}</Link></li>
))}
</ul>
);
}}
</Query>
);
export default AllUsers
Для добавления или изменения состояния на сервере используются Mutation:
import React from 'react';
import { Link } from 'react-router-dom'
import { Mutation } from 'react-apollo';
import gql from 'graphql-tag';
const NewPost = (props) => (
<Mutation
mutation={gql`
mutation createPost($text: String!, $title: String!, $userId: ID!){
createPost(text: $text, title: $title, userId: $userId) {
id
}
}
`}
>
{(createPost, { loading, error, data }) => {
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
let userId, title, text
return (
<form onSubmit={e => {
e.preventDefault();
createPost({ variables: {
userId: userId.value ,
title: title.value ,
text: text.value ,
}});
}}>
<input
type='hidden'
value={ props.match.params.userId }
ref={ node => userId = node }
/>
<input
type='text'
ref={ node => title = node }
/>
<textarea
ref={ node => text = node }
/>
<button type='submit' />
</form>
);
}}
</Mutation>
);
export default NewPost;
Когда я знакомился с примерами компонетов react с использованием apollo graphql client я чувствовал себя не совсем уютно. А все дело в том что разработчик веб-приложения мыслит скорее императивно, чем декларативно. Нам хочется действия. «Установить фильтр», «сохранить запись» и т.п. И когда мы переходим к тому что нам нет необходимости думать о том как данные грузятся с сервера мы вместо того чтобы принять такой подход и использоватьего преимущества задумываемся над тем как мы будем контролировать store.
Дополняю текст еще одним примером, в котором в запросе graphql используется параметр принятый от охватывающего компонента (роута).
import React from 'react';
import { Link } from 'react-router-dom'
import { Query } from 'react-apollo';
import gql from 'graphql-tag';
const Post = (props) => (
<Query
query={gql`
query {
Post(id: "${props.match.params.postId}") {
id
title
text
}
}
`}
fetchPolicy='network-only'
>
{({ loading, error, data }) => {
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
return (
<ul key='topPosts'>
<li>{data.Post.id}</li>
<li>{data.Post.title}</li>
<li>{data.Post.text}</li>
</ul>
);
}}
</Query>
);
export default Post;
Исходный код.
Также Apollo client работает и в условиях серверного рендеринга и универсальных приложений. Предзагрузка данных для серверного рендеринга (включая и все вложенные компоненты) реализована «из коробки». Парадокс в том что соединив несколько технологий которые часто критикуют за усложненность получили в результате существенное упрощение приложения.
apapacy@gmail.com
11 мая 2018 года.
Комментарии (7)
defint
11.05.2018 08:18+1Если уж совсем от redux хочется избавится, то можно попробовать apollo-link-state (https://github.com/apollographql/apollo-link-state).
На данный момент мне не очень понравилась работа с кэшем, но обещают в новой версии добавить хелперы на все случаи жизни.apapacy Автор
11.05.2018 09:01Я думаю что такие решения как apollo-link-state это развитие темы в немного другую сторону — от чистой декларативности к управлению состоянием. Мои планы в ближайшее время посмотреть в сторону relay. Я его долгое догонял но сейчас кажется есть небольшая надежда
iit
В данном примере все просто, но вот если в параметрах запроса есть параметры которые завязаны на кучу других компонентов (поиск, сортировка или фильтрация) то все будет не так тривиально. Про связь мутация+форма+валидация я вообще промочу.
В моем случае пришлось написать подобие orm для всего этого и обернуть её в redux, хотя сейчас я бы лучше использовал для этого mobx.