Intended outcome:
componentWillReceiveProps(newProps) should be called once
Actual outcome:
componentWillReceiveProps(newProps) is being called infinitely and my prev/next comparison "if statements" to try and dismiss the majority of 'newProps' handling, have become too expensive/slow, not to mention it being a bandaid approach that ineffectively patches the problem, rather than eliminating the problem.
How to reproduce the issue:
const mapStateToProps = state => ({
chats: state.chats,
unreadCount: state.unreadCount
})
const ChatContainer = compose(
graphql(GetChatsForDistributor,{
name: 'getChatsForDistributor',
options: props => ({
variables: {
distributorId: { id: props.user.distributorx.id },
shopperId: { id: props.user.shopperx.id }
},
fetchPolicy: 'network-only'
})
}),
graphql(GetChatsForShopper,{
name: 'getChatsForShopper',
options: props => ({
variables: {
shopperId: { id: props.user.shopperx.id }
},
fetchPolicy: 'network-only'
})
})
)(Chat)
export default connect(mapStateToProps,{
setChats,
markUnread,
markRead,
incrementUnreadCount,
decrementUnreadCount
})(ChatContainer)
Version
this could be a redux issue and not necessarily a react-apollo issue... still investigating
@quadsurf Did you find the solution? I'm facing the same problem.
I've face this issue too on my recent big project and quite painful to find the root cause. Luckily i've found how to solve it.
The order of the enhancer is matters. It was caused by every redux state change, it will trigger the component re-render because redux assume your component is pure.
simply do it like:
const enhance = compose(
graphql(...),
graphql(...),
connect(...)
);
export default enhance(Chat);
and make your Chat pure component instead normal component.
import React, { PureComponent } from 'react';
class Chat extends PureComponent {
....
}
Hope it helps.
Closing - housekeeping
I have the same issue when trying to do ssr. I described my problem here on sf
The thing is that I'm not using redux, I'm using apollo cache.
Also as mentioned in the sf post, if I try to display a simple div, there's no loop, but when I try a component that uses gql queries, that same component is rendered indefinitely making getDataFromTree() never end
This is my code to show the issue:
app.get("/", (req, res) => {
console.log("getting ", req.originalUrl, " : ", req.url)
const client = new ApolloClient({
ssrMode: true,
// Remember that this is the interface the SSR server will use to connect to the
// API server, so we need to ensure it isn't firewalled, etc
link: createHttpLink({
uri: 'http://localhost:4000/graphql',
credentials: 'same-origin',
headers: {
cookie: req.header('Cookie'),
},
}),
resolvers: {},
cache: new InMemoryCache(),
});
cacheManager.client = client
const context = {};
const SimpleDivs = () =>
(<div>
in simple cases
<div>
rendering is ok
</div>
</div>
)
const WithQuery = () => {
const LOCAL_CACHE = gql`
query localCache {
name @client
eid @client
previewId @client
kind @client
}
`
console.log( "with query...." )
return (
<Query query={LOCAL_CACHE}>
{({data, client}) => {
return (<div>query case returned name: {data.name}</div>)
}}
</Query>
)
}
// The client-side App will instead use <BrowserRouter>
const App = (
<ApolloProvider client={client}>
<StaticRouter location={req.url} context={context}>
<WithQuery />
</StaticRouter>
</ApolloProvider>
);
console.log("### getDataFromTree ### to be called")
// rendering code
getDataFromTree(App).then(() => {
// We are ready to render for real
console.log("### getDataFromTree ### executed")
const content = ReactDOM.renderToString(App);
const initialState = client.extract();
const html = <Html content={content} state={initialState} />;
res.status(200);
res.send(`<!doctype html>\n${ReactDOM.renderToStaticMarkup(html)}`);
res.end();
});
});
When I replace the component WithQuery with the SimpleDivs one, every thing works fine. but as in the code here, the app shows this log
### getDataFromTree ### to be called
with query....
with query....
with query....
with query....
with query....
(undefinitely)
Please notice that the code works when I remove the method above from my routes and attemp to display through the client