Skip to content

Error - Make sure the relay property on the React context conforms to the RelayEnvironment interface. #189

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

Closed
hung-phan opened this issue Aug 8, 2016 · 16 comments

Comments

@hung-phan
Copy link

hung-phan commented Aug 8, 2016

Hi, I ran into some problems with the new version (Not very sure about this). Basically, I got the error:

invariant.js:38 Uncaught Invariant Violation: RelayContainer: `Relay(onlyUpdateForKeys(Todos))` was rendered with invalid Relay context `undefined`. Make sure the `relay` property on the React context conforms to the `RelayEnvironment` interface.
import React from 'react';
import Relay from 'react-relay';
import useRelay from 'react-router-relay';
import { Router, Route, applyRouterMiddleware } from 'react-router';
import ViewerQuery from 'client/queries/viewer';
import Todos from 'client/components/todos/index.jsx';
import StaticPage from 'client/components/static-page/index.jsx';

export const getClientHistory = () =>
  require('react-router').browserHistory;

export const getServerHistory = (url) =>
  require('react-router').createMemoryHistory(url);

export const getRoutes = (history) => (
  <Router
    history={history}
    render={applyRouterMiddleware(useRelay)}
    environment={Relay.Store}
  >
    <Route path="/" component={Todos} queries={ViewerQuery} />
    <Route path="/static-page" component={StaticPage} />
  </Router>
);
// @flow
import React from 'react';
import Relay from 'react-relay';
import { compose, onlyUpdateForKeys } from 'recompose';
import { createContainer } from 'recompose-relay';
import AddTodoMutation from 'client/mutations/add-todo';
import CompleteTodoMutation from 'client/mutations/complete-todo';
import RemoveTodoMutation from 'client/mutations/remove-todo';
import TodosHeader from './todos-header';
import TodosAdd from './todos-add';
import TodosBody from './todos-body';
import TodosFooter from './todos-footer';
import type { ViewerType } from './types';

export const Todos = ({ viewer, relay }: { viewer: ViewerType, relay: Object }) => (
  <div className="container">
    <div className="row">
      <TodosHeader />
      <TodosAdd relay={relay} viewer={viewer} />
      <TodosBody viewer={viewer} />
      <TodosFooter />
    </div>
  </div>
);

Todos.propTypes = {
  viewer: React.PropTypes.object,
  relay: React.PropTypes.object,
};

export const enhance = compose(
  createContainer({
    initialVariables: {
      numberOfTodos: 10,
    },
    fragments: {
      viewer: () => Relay.QL`
        fragment on Viewer {
          todos(last: $numberOfTodos) {
            edges {
              node {
                id
                text
                complete
              }
            }
          }
          numberOfTodos
          ${AddTodoMutation.getFragment('viewer')}
          ${CompleteTodoMutation.getFragment('viewer')}
          ${RemoveTodoMutation.getFragment('viewer')}
        }
      `,
    },
  }),
  onlyUpdateForKeys(['viewer', 'relay'])
);

export default enhance(Todos);

I use createContainer from recompose, and I don't know how I got this error. I just got this error recently. By the way, this is my project https://github.com/hung-phan/koa-react-isomorphic/tree/features/relay

@hung-phan
Copy link
Author

hung-phan commented Aug 8, 2016

Oh, I figure out the error.

I have to change

  <Router
    history={history}
    render={applyRouterMiddleware(useRelay)}
    environment={Relay.Store}
  >

to

  <Router
    history={history}
    render={applyRouterMiddleware(useRelay.default)}
    environment={Relay.Store}
  >

I wonder why don't we just use export const statement. Thanks for this project

@taion
Copy link
Member

taion commented Aug 8, 2016

That shouldn't be necessary. How are you transpiling your import statements?

@taion
Copy link
Member

taion commented Aug 8, 2016

> require('react-router-relay')
{ renderRouterContext: [Function: renderRouterContext],
  renderRouteComponent: [Function: renderRouteComponent] }

@hung-phan
Copy link
Author

hung-phan commented Aug 8, 2016

Thanks for gettting back to me. I used the webpack 2 to transpile the import statement with the following config.

.babelrc

  {
  "presets": ["es2015", "react", "stage-0"],
...

Here is my config for webpack.

default.js

'use strict';

const path = require('path');
const ROOT = require('./../../path-helper').ROOT;
const config = require('./../../index');
const webpack = require('webpack');

module.exports = {
  context: ROOT,
  entry: {
    app: [
      path.join(ROOT, config.path.app, 'app'),
    ],
  },
  output: {
    path: path.join(ROOT, config.path.publicAssets),
  },
  externals: [],
  resolve: {
    extensions: ['', '.js', '.jsx'],
    modules: [
      path.resolve('./app'),
      'node_modules',
    ],
  },
  module: {
    loaders: [
      {
        test: /\.async\.jsx$/,
        loader: 'react-proxy-loader!exports-loader?exports.default',
      },
      {
        test: /.(js|jsx)$/,
        exclude: /node_modules/,
        loaders: ['babel-loader', 'eslint-loader'],
      },
      {
        test: /\.(gif|jpg|jpeg|png|svg|ttf|eot|woff|woff2)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        loader: 'file-loader',
      },
    ],
  },
  plugins: [
    new webpack.optimize.AggressiveMergingPlugin(),
    new webpack.optimize.OccurrenceOrderPlugin(),
    new webpack.DefinePlugin({
      'process.env.RUNTIME_ENV': "'client'",
    }),
  ],
  eslint: {
    emitWarning: true,
  },
};

development.js

'use strict';

const _ = require('lodash');
const ROOT = require('./../../path-helper').ROOT;
const config = require('./../../index');
const cssnext = require('postcss-cssnext');
const webpack = require('webpack');
const WebpackIsomorphicToolsPlugin = require('webpack-isomorphic-tools/plugin');
const webpackIsomorphicToolsPlugin = new WebpackIsomorphicToolsPlugin(
  require('./../../webpack/webpack-isomorphic-tools')
);
const developmentConfig = require('./default');

_.mergeWith(developmentConfig, {
  output: {
    publicPath: `http://localhost:8080${config.path.build}`,
    filename: '[name].js',
    chunkFilename: '[id].js',
  },
  cache: true,
  debug: true,
  outputPathinfo: true,
  devtool: 'source-map',
  devServer: {
    contentBase: ROOT,
    noInfo: true,
    hot: true,
    inline: true,
  },
  postcss() {
    return [cssnext()];
  },
}, (obj1, obj2) =>
  _.isArray(obj2) ? obj2.concat(obj1) : undefined
);

developmentConfig.module.loaders.push(
  {
    test: /\.css$/,
    loader: `style!css${config.cssModules}!postcss`,
  },
  {
    test: /\.less$/,
    loader: `style!css${config.cssModules}!postcss!less`,
  },
  {
    test: /\.scss$/,
    loader: `style!css${config.cssModules}!postcss!sass`,
  }
);

developmentConfig.plugins.push(
  // new webpack.optimize.CommonsChunkPlugin('common', 'common.bundle.js'), // Code splitting
  new webpack.DefinePlugin({
    'process.env.NODE_ENV': "'development'",
    'process.env.SERVER_RENDERING': process.env.SERVER_RENDERING || false,
  }),
  webpackIsomorphicToolsPlugin.development(),
  new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin() // Hot Module Replacement
);

module.exports = developmentConfig;

production.js

'use strict';

const _ = require('lodash');
const config = require('./../../index');
const cssnext = require('postcss-cssnext');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const ChunkManifestPlugin = require('chunk-manifest-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const WebpackIsomorphicToolsPlugin = require('webpack-isomorphic-tools/plugin');
const webpackIsomorphicToolsPlugin = new WebpackIsomorphicToolsPlugin(
  require('./../../webpack/webpack-isomorphic-tools')
);
const productionConfig = require('./default');

_.mergeWith(productionConfig, {
  devtool: false,
  output: {
    publicPath: config.path.assets,
    filename: '[name]-[chunkhash].bundle.js',
    chunkFilename: '[id]-[chunkhash].bundle.js',
  },
  postcss() {
    return [cssnext()];
  },
}, (obj1, obj2) =>
  _.isArray(obj2) ? obj2.concat(obj1) : undefined
);

productionConfig.module.loaders.push(
  {
    test: /\.css$/,
    loader: ExtractTextPlugin.extract({
      fallbackLoader: 'style',
      loader: `css${config.cssModules}!postcss`,
    }),
  },
  {
    test: /\.less$/,
    loader: ExtractTextPlugin.extract({
      fallbackLoader: 'style',
      loader: `css${config.cssModules}!postcss!less`,
    }),
  },
  {
    test: /\.scss$/,
    loader: ExtractTextPlugin.extract({
      fallbackLoader: 'style',
      loader: `css${config.cssModules}!postcss!sass`,
    }),
  }
);

productionConfig.plugins.push(
  // new webpack.optimize.CommonsChunkPlugin('common', 'common-[chunkhash].bundle.js'), // Code splitting
  new webpack.DefinePlugin({
    'process.env.NODE_ENV': "'production'",
    'process.env.SERVER_RENDERING': true,
  }),
  new ExtractTextPlugin({
    filename: '[name]-[contenthash].css',
    allChunks: true,
  }),
  new ChunkManifestPlugin({
    filename: 'webpack-common-manifest.json',
    manfiestVariable: 'webpackManifest',
  }),
  new webpack.LoaderOptionsPlugin({
    minimize: true,
    debug: false,
  }),
  new webpack.optimize.UglifyJsPlugin({
    compress: {
      warnings: false,
    },
    output: {
      comments: false,
    },
    sourceMap: false,
  }),
  new CompressionPlugin(),
  webpackIsomorphicToolsPlugin
);

module.exports = productionConfig;

I used separate config files for development and production. The weird thing is that it ran fine in production and threw error in development. So that currently, I have to use a workaround

// workaround problem with webpack 2
export const getRenderer = () =>
  applyRouterMiddleware(
    process.env.RUNTIME_ENV === 'client' && process.env.NODE_ENV === 'development'
      ? useRelay.default
      : useRelay
  );

So far, I looked at the problem, it is may related to the webpack 2 `"module": "es/index.js". But I am not sure why it behaves differently

@hosmelq
Copy link

hosmelq commented Aug 8, 2016

Same problem here with 0.13.4 :(

@hung-phan hung-phan reopened this Aug 8, 2016
@taion
Copy link
Member

taion commented Aug 8, 2016

You need to turn off the CommonJS modules transform in Babel if you're using webpack 2.

@taion taion closed this as completed Aug 8, 2016
@hung-phan
Copy link
Author

Uhm, @taion. I did try the es2015-webpack as recommended but I got this error

Module build failed: Error: Cannot remove "babel-plugin-transform-es2015-modules-commonjs" from the plugin list.
    at Object.<anonymous> (/Users/hungphan/Desktop/workarea/koa-react-isomorphic/node_modules/babel-preset-es2015-webpack/index.js:35:11)
    at Module._compile (module.js:409:26)
    at Object.Module._extensions..js (module.js:416:10)
    at Module.load (module.js:343:32)
    at Function.Module._load (module.js:300:12)
    at Module.require (module.js:353:17)
    at require (internal/module.js:12:17)
    at /Users/hungphan/Desktop/workarea/koa-react-isomorphic/node_modules/babel-core/lib/transformation/file/options/option-manager.js:324:15
    at Array.map (native)
    at OptionManager.resolvePresets (/Users/hungphan/Desktop/workarea/koa-react-isomorphic/node_modules/babel-core/lib/transformation/file/options/option-manager.js:305:20)
 @ multi app

@taion
Copy link
Member

taion commented Aug 8, 2016

You should resolve that error, then. Though with recent Babel versions you don't even need es2015-webpack.

@hung-phan
Copy link
Author

Oh, I don't know this. Thanks @taion

@hung-phan
Copy link
Author

For who is looking for the answer. ["es2015", { "modules": false }]

@elado
Copy link

elado commented Aug 9, 2016

["es2015", { "modules": false }] seems to turn off import?

I get SyntaxError: Unexpected token import.

Here's my .babelrc

{
  "passPerPreset": true,
  "presets": [
    { "plugins": [ "./babelRelayPlugin" ] },
    "react",
    ["es2015", { "modules": false }],
    "stage-0"
  ],
  "plugins": [
    "transform-runtime",
    "transform-decorators-legacy",
    "transform-class-properties",
    "react-hot-loader/babel"
  ]
}

I'm using the latest babel+webpack beta 20.

@taion
Copy link
Member

taion commented Aug 9, 2016

webpack 2 has native support for ES modules. As such, you need to (and should) turn off the CommonJS modules transform, as this package is set up to expose an ES module build to bundlers that support it, which apparently leads to weird things happening if you are using the CommonJS module transform.

If you're running your code through Node, you need to enable the CommonJS module transform, as Node does not support CommonJS modules.

@elado
Copy link

elado commented Aug 9, 2016

@taion thanks. How does one do that? The suggested solution (modules: false) seems to cause an error about imports.

@taion
Copy link
Member

taion commented Aug 9, 2016

See e.g. https://github.com/reactjs/react-router/blob/master/tools/es2015Preset.js, but set up the branches appropriately for the conditions in which you want to use or not use the CommonJS module transform.

@bsr203
Copy link

bsr203 commented Aug 10, 2016

@hung-phan @taion Hi I have my setup as

{
  "env": {
    "development": {
      "passPerPreset": true,
      "presets": [
        { "plugins": ["./build/babelPluginRelay"] },
        "react",
        ["es2015", { "modules": false }],
        "stage-0"
      ],
      "plugins": [
        "react-hot-loader/babel",
      ]
    },
...

but still getting original error like Error - Make sure therelayproperty on the React context conforms to theRelayEnvironmentinterface. Can you please let me know if I have the right syntax for presets. thanks a ton.

@hung-phan
Copy link
Author

Hi @bsr203, see this project if that helps. You will see my config for .babelrc and the usage of react-router-relay(https://github.com/hung-phan/koa-react-isomorphic/blob/features/relay/app/routes.jsx). Also this is my component https://github.com/hung-phan/koa-react-isomorphic/blob/features/relay/app/client/components/todos/index.jsx.

And the better way is that you should show your repo or gist. We cannot help you just by looking at your babel config

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants