react-apollo causing infinite re-render loop

Created on 18 Feb 2018  路  6Comments  路  Source: apollographql/react-apollo

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

All 6 comments

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

Was this page helpful?
0 / 5 - 0 ratings