Arthur W
Arthur W

Reputation: 62

Variable Undefined For Straightforward React Hooks Axios Fetch Demo App

I am writing a straightforward react app with a very basic api (git hub repo: https://github.com/mrarthurwhite/use_effect_react_hooks_demo). Following is the functional component which is a demo component meant to illustrate a fetch (with axios), using the useEffect hook, followed by just displaying the data.

import './App.css';
import React, {  useEffect, useState } from 'react';
import axios from 'axios';

function GetWordsWAxiosNLoading() {
  const [words, setWords] = useState([]);
  let isLoading = false;
  let [isLoadings, setIsLoadings] = useState(false); // initially using state variable
  console.log("isLoading prefetch " + isLoading); 

  async function fetchData(){
    //setIsLoading(true);
    isLoading = true;
    console.log("isLoading fetching " + isLoading); 
    let url = 'http://localhost:1111/wordlist';
    const result= await axios(url);
    setWords(result.data);
    //setIsLoading(false);
    isLoading = false;
    console.log("isLoading resetting " + isLoading); 
  };

  useEffect(() => {fetchData()}, [] );
  console.log("isLoading postfetch " + isLoading); 
    return (
    <>
    { isLoading? (<div>Loading . . . </div>) : (     {words.map(w=> <div>{w.word}</div>)}    ) }
    </>
  );
}

export default GetWordsWAxiosNLoading;

The error I am getting is :

./src/GetWordsWAxiosNLoading.js
SyntaxError: use_effect_react_hooks_demo/use_effect_initial_demo/src/GetWordsWAxiosNLoading.js: Unexpected token (27:59)

  25 |     return (
  26 |     <>
> 27 |     { isLoading? (<div>Loading . . . </div>) : (     {words.map(w=> <div>{w.word}</div>)}    ) }
     |                                                            ^
  28 |     </>
  29 |   );
  30 | }

At line 27 above it is giving both a Line 27:60: Parsing error: Unexpected token & a SyntaxError.

I have working variants of the above :

  1. where I am just using fetch instead of axios httpclient (https://github.com/mrarthurwhite/use_effect_react_hooks_demo/blob/master/use_effect_initial_demo/src/App.js), & it works fine but initially it was giving me similar errors (words was undefined & it could not find .map) indicating that the errors returned are stock & perhaps not very descriptive nor accurate.
  2. where I am using axios but without a loading variable ( https://github.com/mrarthurwhite/use_effect_react_hooks_demo/blob/master/use_effect_initial_demo/src/GetWordsWAxios.js) & it works fine

The issues are:

  1. there are no console log outputs (probably because it is not compiling)
  2. the isLoading variable is undefined (I was initially using isLoadings a variable stored in the state object but decided to simplify it).

Any ideas as to what could be going on?

Upvotes: 1

Views: 147

Answers (2)

jmk
jmk

Reputation: 1610

isLoading should be state of component

function GetWordsWAxiosNLoading() {
  const [words, setWords] = useState([]);
  let [isLoading, setIsLoading] = useState(false);

  async function fetchData() {
    setIsLoading(true);
    const result = await axios("http://localhost:1111/wordlist");
    setWords(result.data);
    setIsLoading(false);
  }

  useEffect(() => {
    fetchData();
  }, []);

  return (
    <>
      {isLoading ? (
        <div>Loading . . . </div>
      ) : (
        words.map((w) => <div>{w.word}</div>)
      )}
    </>
  );
}

export default GetWordsWAxiosNLoading;

Upvotes: 0

CertainPerformance
CertainPerformance

Reputation: 371168

You need to be careful and cognizant of when you're in a section where you're using JSX syntax, and when you're in a section of plain JavaScript expressions (which may include JSX elements).

When writing JSX, you can interpolate JavaScript expressions by enclosing them in {}s, eg:

<div className={someClassName}

or

<span>{someText}</span>

That's the only time you should be using {} as delimiters for interpolation. Here:

    <>
    { isLoading? (<div>Loading . . . </div>) : (     {words.map(w=> <div>{w.word}</div>)}    ) }
    </>

The first { is needed to go from the JSX fragment to a standard JS expression, but in the second part of the conditional, you're not starting from a JSX context, so you shouldn't have {. This:

: (     {words.map

should be

: words.map

You should also remove the isLoading plain variable, and use the isLoadings stateful variable instead. To change state in React, call the state setter - you should almost never be reassigning the value of a plain variable.

Cleaning up your code a bit, try:

function GetWordsWAxiosNLoading() {
    const [words, setWords] = useState([]);
    const [isLoading, setIsLoading] = useState(true);
    async function fetchData() {
        const url = 'http://localhost:1111/wordlist';
        const result = await axios(url);
        setWords(result.data);
        setIsLoading(false);
    }
    useEffect(fetchData, []);
    return isLoading
        ? <div>Loading . . . </div>
        : words.map(w => <div>{w.word}</div>);
}

If the words array returned by the API always contains at least one item, you could also remove the isLoading state entirely and just check words.length instead. If 0, it's still loading.

Upvotes: 2

Related Questions