An extremely minor issue and only tried this with createFragmentContainer so far:
relay-compiler can't cope with the the container factory function call not living alongside the graphql literal. For example, we attempted to create a wrapper to make the API a little more friendly when it came to HOC composition:
function createFragmentContainer(fragmentSpec) {
return (Component) => Relay.createFragmentContainer(Component, fragmentSpec)
}
const withFragmentContainer = createFragmentContainer(
graphql`
fragment Module_prop on SomeType {
id
}
`
)
export default compose(
withRouter,
// ...
withFragmentContainer,
)(SomeComponent)
This will work fine when the wrapped createFragmentContainer lives in the same module as the graphql literal, but as soon as its extracted for reuse, we see the following error:
TypeError: Cannot read property 'type' of undefined
at CallExpression (/node_modules/relay-compiler/bin/relay-compiler:4581:21)
at visit (/node_modules/relay-compiler/bin/relay-compiler:4748:6)
at traverse (/node_modules/relay-compiler/bin/relay-compiler:4761:8)
at visit (/node_modules/relay-compiler/bin/relay-compiler:4751:4)
at /node_modules/relay-compiler/bin/relay-compiler:4765:12
at Array.forEach (native)
at traverse (/node_modules/relay-compiler/bin/relay-compiler:4763:13)
at visit (/node_modules/relay-compiler/bin/relay-compiler:4751:4)
at /node_modules/relay-compiler/bin/relay-compiler:4765:12
at Array.forEach (native)
Thanks :)
react-relay v1.0.0
relay-compiler v1.0.0
babel-plugin-relay v1.0.1
@casparrolfe is this the same or similar as my SO question? I cannot relay-compile my apps that reuse a common ui library.
Same issue:
import Relay from 'react-relay/compat'
const createFragmentContainer = (containerConfig: FragmentContainerConfig) =>
(component: ReactClass<*>) => Relay.createFragmentContainer(component, containerConfig.fragments)
export default createFragmentContainer
// separate file
class MyComponent extends Component {...}
const compose = _.flow(
reduxForm({ form }),
connect(select),
createFragmentContainer({ fragments }),
)
export default compose(MyComponent)
Relay compiler output:
Parse error: TypeError: Cannot read property 'type' of undefined ...
error Command failed with exit code 1.
It only works when I use createFragmentContainer directly from Relay:
const compose = _.flow(
reduxForm({ form }),
connect(select),
// createFragmentContainer({ fragments }),
)
export default Relay.createFragmentContainer(compose(PaycheckProratedWages), fragments)
This one seems to be related https://github.com/facebook/relay/issues/1698
@casparrolfe any luck with this issue ?
I'd be curious if that's intended behavior (I hope not). If not, I'd look into creating a fix for it (@leebyron)
There is this project: https://github.com/ErrorPro/relay-compose, that seems to provide HOCs around relay modern containers. That makes me wonder why it doesn't work for me when I try to create a HOC my self. Almost certainly I'm doing something wrong, or is it because I'm using compat. I'm desperately looking for a solution for this issue, having a HOC around createFragmentContainer for instance, will be very handy, specially when your component is wrapped within other HOCs:
const compose = _.flow(
reduxForm({ form: formId }),
connect(mapStateToProps),
createFragmentContainer(fragments),
)
export default compose(MyComponent)
Any hints about this relay-compiler behavior, or workaround suggestions will be much appreciated.
One particularily weird thing in one of my projects is that this bug happens only ~2/3 of the time. Unfortunately I don't have the time atm to create a minimal example that reproduces that indeterminism. Just thought it might be interesting for anyone looking into this.
@maikelrobier I tried the same implementing a recompose helper and got the same error Cannot read property 'type' of undefined.
The problem is in this line and the if clause before.
if (
!(
(callee.type === 'Identifier' &&
CREATE_CONTAINER_FUNCTIONS[callee.name]) ||
(callee.kind === 'MemberExpression' &&
callee.object.type === 'Identifier' &&
callee.object.value === 'Relay' &&
callee.property.type === 'Identifier' &&
CREATE_CONTAINER_FUNCTIONS[callee.property.name])
)
) {
traverse(node, visitors);
return;
}
const fragments = node.arguments[1];
if (fragments.type === 'ObjectExpression') {
...
where CREATE_CONTAINER_FUNCTIONS is defined as
const CREATE_CONTAINER_FUNCTIONS = {
createFragmentContainer: true,
createPaginationContainer: true,
createRefetchContainer: true,
};
So the if clause checks, if the current node in the AST (e.g. the function currently under investigation by the compiler) is not one of createFragmentContainer or Relay.createFragmentContainer (and the other create container functions).
If it is, it gets the second argument and expects a expression tagged with graphql.
So basically, if you use one of the above names for your HOC (createFragmentContainer, ...) and your graphql expression is not the second argument, the compiler will throw an error.
A workaround is to give your HOC a different name like fragmentContainer or something.
@leebyron I think it would be nice to allow custom HOC to use the same name with different arguments. But at least we should add a null check after the line I mentioned:
if (fragments && fragments.type === 'ObjectExpression') {
Then we would get a nice error message in the following else clause (where there is in fact a null check).
Unfortunately this is a current restriction of Relay's babel plugin in how it looks for uses of Relay and graphql.
I would be more than happy to review a pull request that improved things for HOC usage, however I don't think our team will be able to make this change ourselves any time soon.
Closing this issue now since the discussion has taken it's course.
For future searchers, it seems to matter whether the string "graphql" appears anywhere in the source file, as to whether calls to createFragmentContainer are flagged in this way.