Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relay Modern example #1757

Closed
sedubois opened this issue Apr 19, 2017 · 14 comments
Closed

Relay Modern example #1757

sedubois opened this issue Apr 19, 2017 · 14 comments
Labels
examples Issue/PR related to examples

Comments

@sedubois
Copy link
Contributor

With the announcement of Relay Modern, it would be great to assemble an example of Next.js + Relay. According to this @josephsavona's comment it should be possible to have SSR.

@Gregoor
Copy link

Gregoor commented Apr 19, 2017

I continued working on it in the repo I created when I was trying to get Relay-Antique to work with Next: https://github.com/Gregoor/next.js-relay-example

Unfortunately I'm currently stuck with this issue: facebook/relay#1631
Will continue working on it, as soon as that is resolved.

@Gregoor
Copy link

Gregoor commented Apr 20, 2017

That particular issue is solved for now, but it's still not there yet. Haven't quite figured out how to get the Environment to accept my hydration. @josephsavona's comment about the possibility of SSR seems more like a description of a better future, as of right now react-relay is still using an unstable_internal API from relay-runtime (/core). So that's what I had to do as well.

@josephsavona
Copy link

@Gregoor Hydration of the environment is straightforward but not documented: the RecordSource object has a toJSON() method, the result of which can be passed back to the constructor to rehydrate.

@josephsavona
Copy link

The larger blocker for SSR is that by default the QueryRenderer and network layer do not read from cache and always effectively force-fetch. i would take a look at how QueryRenderer works, and create a variation that supports SSR.

@brad-decker
Copy link

@Gregoor @josephsavona - As a short term solution to server side rendering for relay modern specifically in next.js... (WIP, don't judge the code too much).

I have a HOC called NextPage that does some generic setup for all our pages:

import React, { Component } from 'react';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import materialTheme from '../lib/materialTheme';
import environment from '../lib/relayEnvironment';


export default query => ComposedComponent => class NextPage extends Component {
  static async getInitialProps(ctx) {
    const { req } = ctx;
    const isServer = !!req;
    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;

    let pageProps = {};

    if (query) {
      const {
        createOperationSelector,
        getOperation,
      } = environment.unstable_internal;
      const operation = createOperationSelector(getOperation(query));
      const result = await new Promise((resolve) => environment.streamQuery({
        operation,
        onNext: () => resolve(environment.lookup(operation.fragment).data),
      }));

      pageProps = {
        ...result,
      };

      if (isServer) pageProps.recordSource = environment.getStore().getSource().toJSON();
    }

    if (ComposedComponent.getInitialProps) {
      pageProps = {
        ...await ComposedComponent.getInitialProps(ctx),
      };
    }

    return {
      ...pageProps,
      // initialState: store.getState(),
      isServer,
      userAgent,
    };
  }

  render () {
    const { userAgent } = this.props;
    return (
      <MuiThemeProvider muiTheme={getMuiTheme(materialTheme, { userAgent })}>
        <ComposedComponent {...this.props} />
      </MuiThemeProvider>
    )
  }
};

Then in my page,

import React, { Component } from 'react';
import { QueryRenderer, graphql } from 'react-relay';
import environment from '../lib/relayEnvironment';
import NextPage from '../decorators/NextPage';
import Layout from '../containers/Layout';

const query = graphql`
  query pagesQuery {
    root {
      properties {
        edges {
          node {
            id
            propertyName
            street
          }
        }
      }
    }
  }
`

class HomePage extends Component {
  render() {
    return (
      <QueryRenderer
        environment={environment}
        query={query}
        render={({error, props}) => {
          console.log(error, props);
          if (error) {
            return <Layout>{error.message}</Layout>;
          } else if (props || this.props) {
            const serProps = props || this.props;
            return (
              <Layout>
                {serProps.root.properties.edges.map(({ node }) => <p>{node.propertyName}</p>)}
              </Layout>
            );
          }
          return <Layout>Loading</Layout>;
        }}
      />
    );
  }
}

export default NextPage(query)(HomePage);

This is definitely a workaround, as we are essentially hijacking the render method of QueryRenderer to render the response from getInitialProps instead of props inside the QueryRenderer scope. This workaround is potentially alleviated by facebook/relay#1760 which will add a lookup prop to QueryRenderer. Also you'll see in my HOC if we are on the server i'm adding recordSource: to the returned props, which will get added into the NEXT_DATA and then i use the value from that to hydrate the store like so:

let initial = {};

// Hydrate the store from __NEXT_DATA__
if (typeof window !== 'undefined' && window.__NEXT_DATA__) {
  initial = window.__NEXT_DATA__.recordSource;
}

const source = new RecordSource(initial);
const store = new Store(source);

@steida steida mentioned this issue Jun 27, 2017
12 tasks
@timneutkens timneutkens added Status: Help Wanted examples Issue/PR related to examples labels Jul 2, 2017
@Gregoor
Copy link

Gregoor commented Jul 13, 2017

Hey @brad-decker, thanks for posting that. Unfortunately I'm not getting it to server-render something. If you have the time, could you check if I went wrong somewhere? Here is the repo:
https://github.com/Gregoor/next.js-relay-example

@steida
Copy link
Contributor

steida commented Jul 29, 2017

If anyone is looking for working solution https://github.com/este/este/blob/d959fb96dac5a3e346a2897ae4f2e81a9948ac17/components/app.js

Note we are fetching query even for page transition. Next.js + Relay Modern = 😻 Finally, after many years, the developers can write JS apps without annoying spinners everywhere. This is the panacea.

As for an implementation. It's based on https://github.com/robrichard/relay-context-provider Thank you! Note it does not support subscriptions, but I think subscriptions don't belong into page query.

@petrvlcek
Copy link
Contributor

Great work @steida 👍 I've prepared a simple example based on your solution. Comments are welcome 🙂

timneutkens pushed a commit that referenced this issue Aug 13, 2017
timneutkens pushed a commit to timneutkens/next.js that referenced this issue Aug 19, 2017
timneutkens pushed a commit to timneutkens/next.js that referenced this issue Aug 19, 2017
timneutkens added a commit that referenced this issue Aug 27, 2017
* Add examples/with-redux-code-splitting. (#2721)

* #1757 Relay Modern Example (#2696)

* Add ReasonML example (#2640)

* Add ReasonML example

* Add a gitignore specifically for the reasonml example

* Allow custom className for <Main /> (#2802)

* 3.0.2

* Remove beta information from the README.

* 3.0.3

* Remove unnecessary lookup in example with emotion (#2731)

* Document SCSS/Less (#2742)

* Document SCSS/Less

* Add missing word

* Add docs for examples dir

* Add extra example

* uppercase J

* Add with pkg example (#2751)

* Add custom server micro example (#2750)

* Ease running multiple examples at the same time with process.env.PORT (#2753)

* Add line-height rule for error page h2 (#2761)

* Add support for fetching multiple translation files (#2743)

* Add support for fetching multiple translation files

* Cleanup

* Clear missed interval (#2611)

* clear missed interval

* remove trailing whitespace

* Relay Modern Example (#1757) (#2773)

* Simplification of Relay Modern Example (#1757) (#2776)

* Use deterministic names for dynamic import (#2788)

* Always use the same name for the same dynamic import.

* Add unit tests for the modulePath generation.

* Allow tests to run correctly on Windows.

* Make the chunk name a bit pretty.

* Fix tests to run on Windows.

* 3.0.4

* Revert "Make the chunk name a bit pretty." (#2792)

This reverts commit 0c9e8cf.

* 3.0.5

* Use _ as the divider for dynamic import name splitter. (#2793)

Using - gives us some weird webpack errors.

* 3.0.6

* next/dynamic Error Message Tweaks (#2798)

* Fixed issue (#2804)

#2800

* docs(material-ui): move the source code to Material-UI repository (#2808)
anaisbetts pushed a commit to anaisbetts/next.js that referenced this issue Sep 5, 2017
@jacobpdq
Copy link

jacobpdq commented Sep 19, 2017

Unfortunately it doesn't work offline :(

@steida
Copy link
Contributor

steida commented Sep 19, 2017

Can someone close the issue please?

@timneutkens
Copy link
Member

@steida sure thing 👌

@ghost
Copy link

ghost commented Nov 14, 2017

Hi @timneutkens , I'm getting this error "Can't load ./cmds plugin:Error: Cannot find module 'graphql-playground/middleware'" with with-relay-modern example. Any idea what's going on?

@petrvlcek
Copy link
Contributor

Hi, @astenmies I'm not aware that this example would depend on GraphQL Playground. Anyway, there must have been some changes in this package recently. If you are using it in your Express server, this should fix it:

dependencies: {
  "graphql-playground-middleware-express": "^1.1.2",
}

and this:

const playground = require('graphql-playground-middleware-express').default
...
app.use('/playground', playground({ endpoint: '/graphql' }))

@Gregoor
Copy link

Gregoor commented Nov 23, 2017

anyone already looking into gettings this to work with React 16? @petrvlcek maybe?

Edit: Okay, update was trivial. Created a PR for it #3365

@lock lock bot locked as resolved and limited conversation to collaborators Dec 1, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
examples Issue/PR related to examples
Projects
None yet
Development

No branches or pull requests

8 participants