apollographql / apollo-client

:rocket:  A fully-featured, production ready caching GraphQL client for every UI framework and GraphQL server.
https://apollographql.com/client
MIT License
19.34k stars 2.65k forks source link

subscribeToMore breaks going from 1.0.0-rc2 --> 1.0.0-rc3 #1516

Closed dfee closed 7 years ago

dfee commented 7 years ago

Intended outcome: Return value of updateQuery in subscribeToMore is passed as new props to component.

Actual outcome: Return value is not passed as new props to component.

How to reproduce the issue: Below is some example code from a component that demonstrates the issue. It largely apes the example in the docs.

import React, { Component, PropTypes } from 'react';
import { compose } from 'react-apollo';
import { gql, graphql } from 'react-apollo';
import { socketConnect } from 'socket.io-react';
import cloneDeep from 'lodash/cloneDeep';

import events from './config/events';

const CurrentUserQuery = gql`
query {
    currentUser {
        firstName
        lastName
        pictureUrl
        thumbnailUrl
        location
    }
}
`

const CurrentUserSubscription = gql`
subscription {
    currentUser {
        firstName
        lastName
        pictureUrl
        thumbnailUrl
        location
    }
}
`

class Auth extends Component {
  static propTypes = {
    data: PropTypes.shape({
        loading: PropTypes.bool.isRequired,
        currentUser: PropTypes.shape({
            firstName: PropTypes.string.isRequired,
            lastName: PropTypes.string.isRequired,
            location: PropTypes.string.isRequired,
            pictureUrl: PropTypes.string.isRequired,
        }),
    }),
    socket: PropTypes.object.isRequired,
  };

    shouldComponentUpdate(nextProps, nextState) {
        console.log('pp', this.props);  // runs in 1.0.0-rc2, not in 1.0.0-rc3
        console.log('np', nextProps);
        return true;
    }

  componentWillReceiveProps(newProps) {
    console.info('newProps', newProps); // runs in 1.0.0-rc2, not in 1.0.0-rc3
    if (newProps.data.loading) return;
    if (this.subscription) return;

    this.subscription = newProps.data.subscribeToMore({
      document: CurrentUserSubscription,
      variables: {},

      // this is where the magic happens.
      updateQuery: (previousResult, { subscriptionData }) => {
        const newResult = cloneDeep(previousResult); // never mutate state!
        newResult.currentUser = subscriptionData.data.currentUser;
        console.info('newResult', newResult);
        console.info(`pr == nr: ${previousResult === newResult}`);
        return newResult;
      },
      onError: (err) => console.error(err),
    });

  }

  onLogout = () => {
    localStorage.removeItem('token');
    const tokenSetEvent = new CustomEvent(events.token_set);
    dispatchEvent(tokenSetEvent);
  }

  render() {
    console.info('render props:', this.props);  // runs a second time in 1.0.0-rc2, not in 1.0.0-rc3
    const { currentUser, loading } = this.props.data;
    if (loading === true) return <div>Loading...</div>;
    if (!!currentUser) {
      return (
        <div>
            <img alt='userPhoto' src={currentUser.thumbnailUrl}/>
            <div>
                <span>{currentUser.firstName} {currentUser.lastName}</span>
                <br/>
                <span>{currentUser.location}</span>
                <br/>
                <button onClick={this.onLogout}>Logout</button>
            </div>
        </div>
      )
    }
    else {
      // dummy code for the report
      return <button>Login</button>;
    }
  }
}

export default compose (
    graphql(CurrentUserQuery),
    socketConnect,
)(Auth);
dfee commented 7 years ago

Closed in favor of https://github.com/apollographql/react-apollo/issues/585