Using Relay Modern v.1.4.1, consider the following List
component.
It takes two props foo
and bar
and renders a Table
component with these props and the result of the GraphQL query:
class List extends React.Component {
constructor(props) {
super(props);
// I accept this.props.foo and this.props.bar
this.renderQuery = this.renderQuery.bind(this);
}
render() {
return <QueryRenderer
environment={this.props.relayEnvironment}
query={SomeQuery}
variables={{
count: 20,
}}
render={this.renderQuery}
/>;
}
renderQuery(readyState) {
console.log("List component props:");
console.log(this.props);
return <Table
foo={this.props.foo} bar={this.props.bar}
data={readyState.data}
/>
}
}
Now, for the initial rendering of the Table
component foo
and bar
are set correctly, however if foo
or bar
change afterwards the new values are not propagated to the Table
component because of the QueryRenderer
.
Since the QueryRenderer
only re-renders if the variables
for SomeQuery
change the renderQuery
method is not triggered because the QueryRenderer
does not see a need for it, so the Table
component stays with the old prop values.
Since passing down props to children is a very common task the fact that the QueryRenderer
is oblivious to the parent's prop changes poses quite a problem. I've been wondering what the solution for this might be and came up with some options:
QueryRenderer
to re-render with the same readyState
as before. This could perhaps be realized using refs
but you would need to check when to re-render by yourself.QueryRenderer
care about my props. This is obviously the right way but I didn't find anything in the documentation that allows this, so I would need to do something like creating a Higher Order Component from the QueryRenderer
or a subclass of it that implements this behavious.Ideally I would like to have a native way to make the QueryRenderer
look at prop changes, something like
<QueryRenderer
environment={this.props.relayEnvironment}
query={SomeQuery}
variables={{
count: 20,
}}
render={this.renderQuery}
propagateProps={this.props}
/>
or
<QueryRenderer
environment={this.props.relayEnvironment}
query={SomeQuery}
variables={{
count: 20,
}}
render={this.renderQuery}
{...this.props}
/>
Any ideas?
Remove your Class component and change the above code to:
const List = (parentProps) => (
<QueryRenderer
environment={parentProps.relayEnvironment}
query={SomeQuery}
variables={{
count: 20,
}}
render={({ props }) => (
<Table
foo={this.parentProps.foo} bar={this.parentProps.bar}
data={props.data}
/>
)}
/>
)
QueryRenderer is already a React Component in itself and you normally do not have to wrap it in a class Component render.
List is now a Pure function component and when it receives new props (parentProps) it will re-render, hence Table with be re-rendered