23

So, I'm trying to set an initial state for an edit component that gets data from the server and now should be editable in the component state. But when I try to do this:

<Query query={POST_QUERY} variables={{ id: this.props.match.params.id }}>
    {({ data, loading, error }) => {
      this.setState({ title: data.title })

I get stuck in an infinite loop since this is in render. Should I not use the component state with the query component? And if not, what is the alternative?

Buda Örs
  • 643
  • 9
  • 28
Andreas
  • 1,064
  • 3
  • 11
  • 24
  • any specific reason to setState inside `Query` component, you can directly pass it to child component – Piyush Bhati May 20 '18 at 17:21
  • @PiyushBhati, in my case, I'm using a live form validator that needs an input controlled by state. – rma Dec 12 '18 at 14:19
  • @rma its not good idea to use setState inside render function. You can use Query Component inside a container and pass the values to another component use `gDSFP` of that component to set it in state and there you can use your validator. – Piyush Bhati Dec 12 '18 at 15:15
  • Totally agree, @PiyushBhati, and thanks. For now, the DanielRearden's solution above is working for me. – rma Dec 12 '18 at 16:50

2 Answers2

24

Whatever component needs this data as state should be rendered inside the Query component, and then have the data passed down to it as a prop. For example:

class MyComponent extends React.Component {
  constructor (props) {
    this.state = {
      title: props.post.title
    }
  }
}

<Query query={POST_QUERY} variables={{ id: this.props.match.params.id }}>
  {({ data, loading, error }) => {
    <MyComponent post={data.post}/>
  }}
</Query>
Daniel Rearden
  • 58,313
  • 8
  • 105
  • 113
  • You must make sure you render MyComponent only when data.post exists and it's not loading. Unless, you will get 'undefined' values most of the times. – Jeff Jan 26 '19 at 08:52
  • @daniel-rearden Hello I have tried your solution but it did not work inside class component. how you define QUERY component outside class . – Rigal Mar 22 '19 at 12:53
  • I don't get how this answers the question. Where is it setting state after fetching the data? – Phani Rithvij Jun 19 '20 at 12:32
20

You can use the onCompleted prop on Querycomponent to set the state. See below example:

class MyComponent extends React.Component {
  constructor (props) {
    this.state = {
      isFirstRender: true
      title: props.post.title
    }
  }
  
  setTitle = title => {
    if (this.state.isFirstRender){
        this.setState({title, isFirstRender: false})
    }
  }
  
  render () {
    return <Query
             query={POST_QUERY}
             variables={{ id: this.props.match.params.id }} 
             onCompleted={data => this.setTitle(data.post.title)}
           >
      {({ data, loading, error }) => {
        <MyComponent post={data.post}/>
      }}
    </Query>
  }
}

Edit:

As the bug of onCompleted firing multiple times has been resolved in the latest version of react-apollo, we can now simply do:

  ...
     <Query
        query={POST_QUERY}
        variables={{ id: this.props.match.params.id }}
        onCompleted={data => this.setState({ title: data.post.title})}
     >
      {({ data, loading, error }) => {
         <MyComponent post={data.post}/>
      }}
    </Query>
  )
  ...
Eesa
  • 2,288
  • 2
  • 24
  • 46
  • Once you set the state the component will rerender and apollo will get the data again, hence and infinite loop. – Remigius Kalimba Jr Apr 04 '19 at 07:38
  • 2
    @RemigiusKalimbaJr No, actually on re-render the condition `if (this.state.isFirstRender)` will be false hence the loop will break – Eesa Apr 04 '19 at 11:20
  • But will it still trigger Query , twice? – Anand May 31 '19 at 01:46
  • @SutikshanDubey This seems to be a bug in `react-apollo` there is an on going issue over here https://github.com/apollographql/react-apollo/issues/2522 – Eesa Jun 01 '19 at 08:07
  • This should be the accepted answer since it adequately answers the question OP asked. You may not always want to render the component inside . Some data elsewhere may rely on its completion first. – Asamoah Nov 18 '20 at 00:17