СоХабр закрыт.

С 13.05.2019 изменения постов больше не отслеживаются, и новые посты не сохраняются.

| сохранено

H Apollo graphql client — разработка изоморфных приложений на react.js в черновиках Tutorial

В предыдущем сообщении Apollo graphql client применялся для разработки чисто клиентского приложения. В документации Apollo graphql client есть (очень лаконичный) раздел, посвященный серверному рендерингу и изоморфным приложениям.

Одна из сложностей серверного рендеринга в react.js является необходимость асинхронной загрузки данных, т.к. серверный рендеринг в reaсt.js — операция синхронная. Например, фреймверк next.js предлагает для этого использовать специальный компонент page в котором реализован дополнительный метод static async getInitialProps(), в котором предлагается проводить асинхронную загрузку данных. Такое решение не лишено недостатков. Например, этот метод статический, следовательно не имеет доступ к экземпляру компонента, метод реализован только для компонента самого верхнего уровня и отсутствует у вложенных компонентов. Решение с Apollo graphql client может быть использовано для компонента произвольного уровня вложенности.

Продолжим работу с проектом который рассматривался в предыдущем сообщении. В серверном коде есть два ключевых момента: использование в качестве корневого элемента компонент ApolloProvider client={client} и асинхронное получение данных функцией await getDataFromTree(App). За кадром ApolloProvider определяет какие запросы нужно выполнить для рендеринга компонентов с учетом выбранного роута, выполняет эти запросы и передает в компоненты при рендеринге.

import React from 'react';
import ReactDOMServer from 'react-dom/server';
import { StaticRouter, Switch, Route, } from 'react-router-dom';
import fetch from 'node-fetch';
import { ApolloProvider, getDataFromTree, renderToStringWithData  } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { createHttpLink } from 'apollo-link-http';
import { InMemoryCache } from "apollo-cache-inmemory";
import assets from '../build/asset-manifest.json';
...

module.exports = async (req, res, next) => {
  const client = new ApolloClient({
   ssrMode: true,
   link: createHttpLink({
     uri: 'https://api.graph.cool/simple/v1/ciyz901en4j590185wkmexyex',
     headers: {
       cookie: req.header('Cookie'),
     },
     fetch,
   }),
   cache: new InMemoryCache(),
 });

    const context = {};
    const App =  <ApolloProvider client={client}>
      <StaticRouter location={req.url} context={context}>
        <Layout>
          <Switch>
            <Route exact path='/' component={ AllUsers } />
            <Route exact path='/posts' component={ TopPosts } />
            <Route exact path='/post/:postId' component={ Post } />
            <Route exact path='/user/:userId' component={ NewPost } />
          </Switch>
        </Layout>
      </StaticRouter>
    </ApolloProvider>;

    await getDataFromTree(App)
    const html = ReactDOMServer.renderToString((App));
    const initialState = client.extract();

    res.write(`
        <!DOCTYPE html>
        <html>
          <head>
            <link rel="stylesheet" href="/${assets['main.css']}">
          </head>
          <body>
            <script>
              window.__PRELOADED_STATE__ = ${JSON.stringify(initialState, null, 2).replace(/</g, '\\u003c')};
            </script>
            <section id="app">${html}</section>
            <script src="/${assets['main.js']}"></script>
          </body>
        </html>
      `);
      res.end();
}


Соответственно, для восстановления данных на клиенте необходимо восстановить состояние клиента передав параметр в конструктор клиента: cache: new InMemoryCache().restore(window.__PRELOADED_STATE__).

import React from 'react';
import { Route, Switch, BrowserRouter } from 'react-router-dom';
import { ApolloProvider } from 'react-apollo';
import ApolloClient from 'apollo-boost';
import { InMemoryCache } from "apollo-cache-inmemory";
...

const client = new ApolloClient({
  uri: 'https://api.graph.cool/simple/v1/ciyz901en4j590185wkmexyex',
  cache: new InMemoryCache().restore(window.__PRELOADED_STATE__),
});

const App = () => (
  <ApolloProvider client={client}>
      <BrowserRouter>
        <Layout>
          <Switch>
            <Route exact path='/' component={ AllUsers } />
            <Route exact path='/posts' component={ TopPosts } />
            <Route exact path='/post/:postId' component={ Post } />
            <Route exact path='/user/:userId' component={ NewPost } />
          </Switch>
        </Layout>
      </BrowserRouter>
  </ApolloProvider>
);

export default App;


Код этого примера я поместил в ветку ssr репозитария.

apapacy@gmail.com
20 мая 2018 года.
+4
~1700

комментарии (0)