2
votes

Using the old way:

graphql(GET_PROJECTS_BY_USER_ID, {
    options: ownProps => ({
        skip: !ownProps.loggedUser.id,
    }),
    props: ({ data }) => {
        return data;
    },
});

I could catch the list of projects inside componentWillReceiveProps lifecycle and dispatch the redux action there:

componentWillReceiveProps(nextProps) {
    if (!nextProps.loading && this.props.loading) {
        this.props.setProjects(nextProps.getProjects);
    }
}

Now I'm trying out Apollo's <Query/> component and I can't figure out how to properly dispatch an action:

render() {
    return (
        <Query
            query={GET_PROJECTS_BY_USER_ID}
            skip={!this.props.loggedUserId}
        >
            {({ loading, data: { getProjects: projects } }) => {
                if (loading) return 'Loading ...';

                this.props.setProjects(projects); // no good

                // return projects
            }}
        </Query>
    );
}

This will cause the following warning:

Cannot update during an existing state transition (such as within render or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to componentWillMount.

The problem is that by using <Query>,componentWillMount` will no longer be called.

1

1 Answers

2
votes

The new Query component uses the render props pattern. The function that's passed in as the child of the component is called inside Query's render method, which means you don't have access to any other lifecycle methods for that component inside the function. To get around that, you have to create an additional component that implements the lifecycle method(s) you need. Just one way to do that:

class DoSomethingOnce extends Component {
  componentWillMount () {
    this.props.action()
  }
  render () {
    return this.props.children || null
  }
}

<Query
  query={GET_PROJECTS_BY_USER_ID}
  skip={!this.props.loggedUserId}
>
  {({ loading, data: { getProjects: projects } }) => {
    if (loading) return 'Loading ...';
    <DoSomethingOnce action={() => setProjects(projects)}>
      // whatever else you're going to render
    </DoSomethingOnce>
  }}
</Query>

Alternatively your component with componentDidMount could just do all the rendering instead of passing the buck to its children. You can always also use a library like reactions/component.

This is really no different than when you were using the graphql HOC -- you still had to wrap that HOC around a component that implemented componentDidMount.