React variable strange behavior - parseInt or props.match.params the cause?

React seems to be behaving very strangely. My guess is that this is because the DOM is painted over when assigning a variable a value of props.match.params. Can someone explain?

My question: when executing a function called useQuery, which is assigned a variable, flight_number, the function executes correctly and returns data. This variable - flight_number - is assigned an integer value of 2.

Why does the function return an error if I replace the variable, flight_number, with another variable, test_variable, also assigned a value of 2, but without first getting that number from props.match.params as a string and then using parseInt?

This returns an error

function Launch(props) {
  let { flight_number } = props.match.params
  console.log("flight_number", flight_number)
  console.log("flight_number type", typeof flight_number)
  flight_number = parseInt(flight_number)
  let test_variable = 2

  const { loading, error, data } = useQuery(LAUNCH_QUERY, {
    variables: { test_variable }
  })

  if (loading) return <p>Loading...</p>
  if (error) return <p>Oops.. try again later..</p>

 return <p>{data.launch.mission_name}<p>

This returns data as desired

function Launch(props) {
  let { flight_number } = props.match.params
  console.log("flight_number", flight_number)
  console.log("flight_number type", typeof flight_number)
  flight_number = parseInt(flight_number)
  let test_variable = 2

  const { loading, error, data } = useQuery(LAUNCH_QUERY, {
    variables: { flight_number }
  })

  if (loading) return <p>Loading...</p>
  if (error) return <p>Oops.. try again later..</p>

 return <p>{data.launch.mission_name}<p>

Here’s the full component - I’m using graphQL

import React, { Component, Fragment, useEffect } from "react"

import { Link } from "react-router-dom"
import classNames from "classnames"
import { useQuery, ApolloConsumer, gql } from "@apollo/client"

const LAUNCH_QUERY = gql`
  query LaunchQuery($flight_number: Int!) {
    launch(flight_number: $flight_number) {
      flight_number
      mission_name
      launch_year
      launch_success
      launch_date_local
      rocket {
        rocket_id
        rocket_name
        rocket_type
      }
    }
  }
`

function Launch(props) {
  let { flight_number } = props.match.params
  console.log("flight_number", flight_number)
  console.log("flight_number type", typeof flight_number)
  flight_number = parseInt(flight_number)
  let test_variable = 2

  const { loading, error, data } = useQuery(LAUNCH_QUERY, {
    variables: { flight_number}
  })
  if (loading) return <p>Loading...</p>
  if (error) return <p>Oops.. try again later..</p>

  const {
    mission_name,
    launch_year,
    launch_success,
    rocket: { rocket_id, rocket_name, rocket_type }
  } = data.launch

  return (
    <div>
      <h1 className='display-4 my-3'>
        <span className='text-dark'>Mission:</span>
        {mission_name}
      </h1>
      <h4 className='mb-3'>Launch Details</h4>
      <ul className='list-group'>
        <li className='list-group-item'>Flight Number: {flight_number}</li>
        <li className='list-group-item'>Launch Year: {launch_year}</li>
        <li className='list-group-item'>
          Launch Successful:{" "}
          <span
            className={classNames({
              "text-success": launch_success,
              "text-danger": !launch_success
            })}
          >
            {launch_success ? "Yes" : "No"}
          </span>
        </li>
      </ul>

      <h4 className='my-3'>Rocket Details</h4>
      <ul className='list-group'>
        <li className='list-group-item'>Rocket ID: {rocket_id}</li>
        <li className='list-group-item'>Rocket Name: {rocket_name}</li>
        <li className='list-group-item'>Rocket Type: {rocket_type}</li>
      </ul>
      <hr />
      <Link to='/' className='btn btn-secondary'>
        Back
      </Link>
    </div>
  )
}

export default Launch

React seems to be behaving very strangely.

In what way? Not to be snarky, but my guess is that it is probably doing exactly what it was programmed to do and it is we that are making the mistake. But still, you need to tell us what is happening.

Why does the function return an error …

What error?

This returns an error

Again, what error are you seeing?


In any case, putting the riddle aside, looking at your code, I see one thing that catches me off guard:

  const { loading, error, data } = useQuery(LAUNCH_QUERY, {
    variables: { test_variable }
  })

You seem to be under the misapprehension that this:

const flight_number = 123;
const test_variable = flight_number;

const myObj1 = {
  variables: { test_variable }
};

const myObj2 = {
  variables: { flight_number }
};

will produce the same object, or at least ones with the equivalent shape and values (deep clones). I would recommend logging out those to see what they are doing. Actually, even if I hadn’t been able to see this probable error, that would have been one of the first things I tried - logging out the data to make sure it is what I assume it is. Once you do this, you should see the problem. You may need to review object shorthand notation.

Of course React doesn’t ever behave strangely. I said it SEEMS to be behaving strangely.

your console.log output as requested

function Launch(props) {
  let { flight_number } = props.match.params
  console.log("flight_number", flight_number)
  console.log("flight_number type", typeof flight_number)
  flight_number = parseInt(flight_number)
  let test_variable = 2

  const myObj1 = {
    variables: { test_variable }
  }

  const myObj2 = {
    variables: { flight_number }
  }

console.log("myObj1", myObj1) //returns the interger 2

  console.log("myObj2", myObj2) //returns the integer 3

  const { loading, error, data } = useQuery(LAUNCH_QUERY, {
    variables: { test_variable }
  })

  if (loading) return <p>Loading...</p>
  if (error) return <p>Oops.. try again later..</p>

 return <p>{data.launch.mission_name}<p>



yet this function returns the name of a SpaceX launch

function Launch(props) {
  let { flight_number } = props.match.params
  console.log("flight_number", flight_number)
  console.log("flight_number type", typeof flight_number)
  flight_number = parseInt(flight_number)
  let test_variable = 2

  const { loading, error, data } = useQuery(LAUNCH_QUERY, {
    variables: { flight_number }
  })

  if (loading) return <p>Loading...</p>
  if (error) return <p>Oops.. try again later..</p>

 return <p>{data.launch.mission_name}<p>

and this returns “Oops… try again later”, because the useQuery returns an error

function Launch(props) {
  let { flight_number } = props.match.params
  console.log("flight_number", flight_number)
  console.log("flight_number type", typeof flight_number)
  flight_number = parseInt(flight_number)
  let test_variable = 2

  const { loading, error, data } = useQuery(LAUNCH_QUERY, {
    variables: { test_variable }
  })

  if (loading) return <p>Loading...</p>
  if (error) return <p>Oops.. try again later..</p>

 return <p>{data.launch.mission_name}<p>

Here’s a working git: GitHub - kevinadhiguna/spacex-rocket-launch-logs: 🚀 A web app that displays SpaceX's rocket launch history

If anyone is curious, go into Launch.js and replace the variable “flight_number” in, variables: { flight_number }, with a different variable assigned to a number between 1 and 100. You will get an error returned from useQuery. I cannot for the life of me see why other than that passing a prop to useQuery may cause some seemingly special behavior.

Am I on a prank show? That is impossible. First of all, they are objects, not integers. And those two objects have different shapes. They have a big difference that has nothing to do with 2s or 3s. Even if those values were the same, they would not be the same. Look at the entire object.

Screen Shot 2021-03-30 at 15.58.44

they look the same shape to me, but yes, I should specify that they are in fact objects. In obj1 a value of test_variable is assigned to a propery of variables - test_variable has a value of 2.

In obj2 a varlue of flight_number is assigned to a property of variables - flight_number has a value of 3.

Am I still missing something?

Right, but they have a different shape.

I don’t know what is causing your error, but that is made extremely difficult because I still have no idea what error you are getting. But, I could see if some function is trying to access obj.variables.flight_number and it doesn’t exist in your test scenario - I could see how that might cause a problem. You aren’t just changing the value of that property, you’re also changing the key of that property. Did you write your code to work for any key, not just “flight_number”?

Bingo! I forgot GraphQL requires that the key is specified. I should have told you I was getting a 500 error. Sorry for wasting your time. I was thinking that the error wasn’t logging but that was just my confusion.

1 Like

Yeah, you might get this to work with:

  const { loading, error, data } = useQuery(LAUNCH_QUERY, {
    variables: { flight_number: test_variable }
  })

And of course a 500 error has nothing to do with React. It is supposed to mean that something went wrong on the server. Of course, if the server isn’t written robustly enough, mistakes that we make in what we send can cause those - it’s just the server should have been written more robustly, should return a 4xx error with a meaningful error message.

What would I add to this to catch this error?

const axios = require("axios")
const {
  GraphQLObjectType,
  GraphQLInt,
  GraphQLString,
  GraphQLBoolean,
  GraphQLList,
  GraphQLSchema
} = require("graphql")

// Launch Type
const LaunchType = new GraphQLObjectType({
  name: "Launch",
  fields: () => ({
    flight_number: { type: GraphQLInt },
    mission_name: { type: GraphQLString },
    launch_year: { type: GraphQLString },
    launch_date_local: { type: GraphQLString },
    launch_success: { type: GraphQLBoolean },
    rocket: { type: RocketType }
  })
})

// Rocket Type
const RocketType = new GraphQLObjectType({
  name: "Rocket",
  fields: () => ({
    rocket_id: { type: GraphQLString },
    rocket_name: { type: GraphQLString },
    rocket_type: { type: GraphQLString }
  })
})

// Root Query
const RootQuery = new GraphQLObjectType({
  name: "RootQueryType",
  fields: {
    launches: {
      type: new GraphQLList(LaunchType),
      resolve(parent, args) {
        return axios
          .get("https://api.spacexdata.com/v3/launches")
          .then(res => res.data)
      }
    },
    launch: {
      type: LaunchType,
      args: {
        flight_number: { type: GraphQLInt }
      },
      resolve(parent, args) {
        return axios
          .get(`https://api.spacexdata.com/v3/launches/${args.flight_number}`)
          .then(res => res.data)
      }
    },
    rockets: {
      type: new GraphQLList(RocketType),
      resolve(parent, args) {
        return axios
          .get("https://api.spacexdata.com/v3/rockets")
          .then(res => res.data)
      }
    },
    rocket: {
      type: RocketType,
      args: {
        id: { type: GraphQLInt }
      },
      resolve(parent, args) {
        return axios
          .get(`https://api.spacexdata.com/v3/rockets/${args.id}`)
          .then(res => res.data)
      }
    }
  }
})

module.exports = new GraphQLSchema({
  query: RootQuery
})

Is it enough to simply add a catch with a message, like so?

 launch: {
      type: LaunchType,
      args: {
        flight_number: { type: GraphQLInt }
      },
      resolve(parent, args) {
        return axios
          .get(`https://api.spacexdata.com/v3/launches/${args.flight_number}`)
          .then(res => res.data).catch(err => err.message)
      }
    },

I haven’t done much GQL and have done none from the server side. But I assume that there is a more graceful way to handle the incorrect shape being sent and to return a more meaningful error code/message.

But in any case, you can at least fix the frontend to send the right object.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.