Search code examples
reactjsapolloreact-apolloapollo-client

React: An component attribute is not properly storing the data (from a query) that I want


I recently started learning react and I have encountered something that I do not understand. So when I declare a component I also declare an attribute in the constructor. Then, after executing the first query (I am using Apollo client - GraphQL ) I want to store the result (which I know that will be always an email) in the attribute declared so I can use it as a parameter in the second query.

The app logic is that I want to show all the orders of a given email, but first I get the email with a query.

Here is the code:

export default class Orders extends Component {
    constructor(){
        super();
        this.email = '';
      }

    render() {
        return (
            <div>
                <Query query = { GET_MAIL_QUERY }>
                {({data, loading}) => {
                    if (loading) return "Loading...";
                    this.email = data.me.email;
                    return <h1>{this.email}</h1>
                 }}

At this point a header containing the email is returned, so all good. But when I execute the second query (or try to display the email in the second header for that matter) it seems that the value is not properly stored.

                </Query>
                <h1>{this.email}</h1>
                <Query query = { GET_ORDERS_QUERY }
                variables = {{
                    email: this.email
                }}>
                    {({data, loading}) => {
                        if (loading) return "Loading...";
                        console.log(data);
                        let orders = data.ordersByEmail.data;
                        console.log(orders);
                        return orders.map(order =>
                            <div>
                                <h1>{order.date}</h1>
                                <h1>{order.price}</h1>
                                <h1>{order.conference.conferenceName}</h1>
                                <h1>{order.user.email}</h1>
                                <br></br>
                            </div>)
                    }}
                </Query>
            </div>
        )
    }
}
const GET_MAIL_QUERY = gql`
query getMyMail{
    me{
      email
    }
  }
`;  

const GET_ORDERS_QUERY = gql`
query getOrdersByEmail($email: String!) {
    ordersByEmail(email: $email) {
      data {
        gid
        date
        price
        user {
            email
          }
        conference{
            conferenceName
        }
      }
    }
  }
`;

I would love an explanation for this and maybe a solution (to store a value returned from a query to use it in another)

Thanks in anticipation :)


Solution

  • In my experience, you should use useQuery imported from @apollo/react-hooks with functional component because it's easy to use, it makes your code more cleaner

    If your want to use <Query/> component with class component, it's ok. But, if you want to store data received from server, you should create a variable in state of constructor and when you want to update to state, you should use this.setState({email: data.me.email}). Don't use this.state.email = data.me.email, it's anti-pattern, React will not trigger re-render when you use it to update your state.

    This is the code:

    import React, { useState } from 'react'
    import gql from 'graphql-tag'
    import { useQuery, useMutation } from '@apollo/react-hooks'
    
    const GET_MAIL_QUERY = gql`
    	query getMyMail {
    		me {
    			email
    		}
    	}
    `
    
    const GET_ORDERS_QUERY = gql`
    	query getOrdersByEmail($email: String!) {
    		ordersByEmail(email: $email) {
    			data {
    				gid
    				date
    				price
    				user {
    					email
    				}
    				conference {
    					conferenceName
    				}
    			}
    		}
    	}
    `
    
    const Orders = () => {
    	const [email, setEmail] = useState('')
    	const { data: getMailQueryData, loading, error } = useQuery(GET_MAIL_QUERY, {
    		onCompleted: data => {
    			setEmail(data.me.email)
    		},
    		onError: err => alert(err),
    	})
    	const { data: getOrdersQueryData } = useQuery(GET_ORDERS_QUERY, {
    		variables: { email: email },
    	})
    
    	if (loading) return <div>Loading...</div>
    	if (error) return <div>Error...</div>
    	return ...
    }