denvned/isomorphic-relay

Invariant Violation when using a decorator

dphaener opened this issue · 7 comments

I'm getting this: Invariant Violation: RelayQueryNode: Abstract class cannot be instantiated. when I attempt to wrap my React component with a decorator. Example:

var relayDecorator = function(component) {
  return Relay.createContainer(component, { fragments: component.fragments })
}

@relayDecorator
export default class Component extends React.Component {
  ...
}

A couple of things to note here. I originally thought this was a Relay and/or Babel 6 issue, but in an attempt to recreate it on a smaller scale (the full blown code that I'm using is in a private project), everything worked fine as long as I wasn't server rendering. As soon as I introduced isomorphic-relay, I started getting these errors.

Thanks for the library btw, I'm loving it so far!

After reading through a couple of related threads, it seems as if I may be able to get around this by crafting a custom network layer for the server side rendering. I'll keep you posted.

@denvned Unfortunately, the custom network layer isn't working. Not sure where the error is, but I don't know that it's isolated to this library. I was able to create a custom network layer that completely removed the need for including isomorphic-relay(in order to hack around the self is not defined error with fetch), but using a decorator still caused this error. Going to close, as it seems to be something with Relay, but probably still worth discussing if anyone else is trying to do this.

Sorry for commenting on a closed issue, but @dphaener do you mind sharing what the related threads you found were? I've run into the exact same issue, not even using decorators but just a simple function.

Essentially my setup is as below, retrieve a list of components that implement a shared interface, and then render a corresponding Relay container represented by a GraphQLEnumType describing the component to render.

This works beautifully in the browser, but I hit the same error that you did when rendering on the server side.

class Renderer extends React.Component {
  renderComponent = (component, index) => {
    switch (component.component) {
      case 'THING':
        return React.createElement(ThingComponent, { component, key: index });
      default:
        return null;
    }
  };

  render() {
    return <div>{this.props.renderables(this.renderComponent)</div>;
  }
}

Relay.createContainer(Renderer, {
  fragments: {
    renderables: () => Relay.QL`
      fragment on RenderableInterface {
        component
      }
    `,
  },
});

Will see if I can get further on this one, but if you've got any insights would be much appreciated.

Specifically, the issue here seems to be including a fragment - logging the name where Relay does its invariant check, and the concreteField that is passed into that function (RelayQueryNode() in RelayQuery.js), it breaks when it encounters:

{ name: 'RelayQueryNode',
  concreteNode:
   RelayFragmentReference {
     _initialVariables: { path: null },
     _fragment: undefined,
     _fragmentGetter: [Function],
     _isContainerFragment: true,
     _isDeferred: false,
     _variableMapping: { path: [Object] },
     _prepareVariables: undefined } }

The Relay container in my example above looks more like:

Relay.createContainer(Renderer, {
  fragments: {
    renderables: () => Relay.QL`
      fragment on RenderableInterface {
        component
        ... on ThingType {
          ${ThingComponent.getFragment('component')}
        }
      }
    `,
  },
});

Going to try refactoring that include to not be inside the spread and see if that fixes it. @dphaener did yours have anything similar in it?

@jgwmaxwell I wasn't using any spreads anywhere, but I was composing using fragments like that. It's been awhile and I can't remember/find the threads I was talking about above. Sorry about that. I ended up just punting on this because it was just a rabbit hole that was neverending for me. I know the Relay team is working on making server side rendering baked in, so I just decided to wait until that happened to see if it was resolved.

Sorry I couldn't help more!

Not at all! Thanks for your time in answering, and sorry to those I've spammed in this!

I had the same issue. I've stepped through a debugger on both the client and server, and have determined in my case the bug is related to instanceof. This line should be true on the server:

https://github.com/facebook/relay/blob/v0.9.0/src/query/RelayQuery.js#L1365

...
 } else if (concreteNode instanceof RelayFragmentReference) {
...

but it's not because RelayFragmentReference comes from a different node_modules/react-relay (I have two of these, one for the server and the other for the client). Since two instances of RelayFragmentReference exist at the same time, they do not pass that check, and therefore the code ultimately fails.

So the solution would seem to be to use the same dependencies on both the server and client.