I'm trying to use react-hot-loader with my Relay modern app. I'm facing an issue with it. The way hot reloading works, I'm rendering AppContainer as root DOM node which renders my Relay QueryRenderer as it's child.
const render = Component => {
ReactDOM.render(
<AppContainer>
<Component />
</AppContainer>,
document.getElementById("root")
);
};
render(RootQueryRenderer);
if (module.hot) {
module.hot.accept("./components/root", () => render(RootContainer));
}
export default () => (
<QueryRenderer
environment={relayEnvironment}
query={graphql`
query rootQuery {
user {
id
}
}
`}
variables={{}}
render={({ error, props, ...rest }) => {
if (props) {
return <Root {...props} />;
} else {
return <div>Loading</div>;
}
}}
/>
);
On every change on hot loading, QueryRenderer re-renders all the components. First it need to return empty loading div and then when the network request if fulfilled Root app gets returned with new props. Return loading div at first re-renders the entire app.
Thanks for posting. Can you clarify: how would you expect/prefer this to work?
Note that QueryRenderer (and all the containers) will forcibly re-render whenever the environment instance, root query, or root variables change. This is to ensure that they are rendering the correct data. If the environment changes, that means different cached data. If the query/variables change, different data is being selected, etc.
I expect during development whenever any changes happen in my app code, for example css of a component is changed, only that component is re-rendered to reflect css changes. This should not re-render entire app. I'm losing entire state of application because I need to return a loading div to QueryRenderer.
In general we didn't really consider hot reloading integration during the development of Relay Modern so this is likely to be a bit rough. As a first step i would look at what is causing QuerRenderer to refetch - is it getting unmounted and remounted? Is it getting new props that cause it to refetch?
Thanks @josephsavona for advice. I found there was two reasons what caused this in my app and was able to get it working.
First, I replaced the functional component to a ReactClass component which renders QueryRenderer and keeps query and environment as state and passes down to query renderer as mentioned in code below
export default class RootQuery extends React.Component {
state = {
query: graphql`
query rootQuery {
user {
...Account_user
}
}
`,
environment: relayEnvironment,
};
render() {
return (
<QueryRenderer
environment={this.state.environment}
query={this.state.query}
variables={{}}
render={({ error, props }) => {
if (props) {
return <Root {...props} />;
} else {
return <div />;
}
}}
/>
);
}
}
This way I was able to ensure QueryRenderer doesn't gets new query or environment when hot reloading happens and it's not doing a refetch. Please let me know if this is a correct way to approach this.
Second, I'm using react-router-v4's withRouter HOC to make my container aware of routes which was remounting my container. I'm still looking into how to make withRouter not remount my container.
Cool, glad you got the Relay portion working. Using a stateful component seems like a reasonable approach.
Moving environment and query into state worked (Relay no longer refetches on a hot reload).
<QueryRenderer
environment={this.state.environment}
query={this.state.query}
...
/>
Thanks @thenaineshgohil!
Most helpful comment
Thanks @josephsavona for advice. I found there was two reasons what caused this in my app and was able to get it working.
First, I replaced the functional component to a ReactClass component which renders
QueryRendererand keepsqueryandenvironmentas state and passes down to query renderer as mentioned in code belowThis way I was able to ensure
QueryRendererdoesn't gets new query or environment when hot reloading happens and it's not doing a refetch. Please let me know if this is a correct way to approach this.Second, I'm using
react-router-v4'swithRouterHOC to make my container aware of routes which was remounting my container. I'm still looking into how to makewithRouternot remount my container.