0

I am writing a react app to fetch vocabulary from a backend database and then display it to the user, along with some information about each particular word. My problem is that I can't get the word to display inside render() as the index of the array is undefined, however according to chrome devtools the array is fully defined.

Here's my code:

import React from 'react';
import "./Vocab.css"

 class Vocab extends React.Component {
   //HEADER HEADER HEADER

   state = {
     vocabulary: null,
     var2: null
   };

   componentDidMount(){
     //Initializing Routine Here
     this.getVocab()
   }

   getVocab = () => {
     var newVocab = [];
     if(this.state.vocabulary == null){
       newVocab = newVocab;
     }
     else{
       newVocab = [this.state.vocabulary];
     }
     this.props.HandleFetch("GET", this.props.backLink + "vocab")
      .then(r => JSON.parse(r))
      .then(data => newVocab.push(data[0]))
      .then(this.setState({vocabulary:newVocab}))
   }

   //In render() variable display and the if/else statements are used exclusively for testing purposes
   render() {
     var display = "X";
     if(this.state.vocabulary == null){
       display = "Y";
     }
     else{
       console.log(this.state.vocabulary); //Outputs the array 
       console.log(this.state.vocabulary[0]) //Outputs "undefined"
       display = "Z";
     }
     return (
       <>
       <h1>Test</h1>
       {display}
       </>
     );
   }
 }
 export default Vocab;

The output of console.log(this.state.vocabulary):

0:
Definition: "TestDef"
IPA: "TestIPA"
IsCard: true
Language: "TestLang"
LastStudyDate: "2021-01-27"
Mnem: ""
PoS: "TestPO"
Source: "TestDict"
SourceLearned: "TestSource"
Word: "Test"

The output of this.state.vocabulary[0]:

undefined

Additionally, according to chrome devtools and through typing $r.state.vocabulary[0] in the console I get the dictionary at the array's index, which I need:

{Word: "Test", Mnem: "", LastStudyDate: "2021-01-27", SourceLearned: "TestSource", IsCard: true, …}

In the console the array is fully navigable, but the web page itself won't render it. Any idea what I might be doing wrong?

UPDATE I was able to add a button the onClick goes to a function which prints out vocabulary and vocabulary[0]. This DOES work, but the render() function and componentDidUpdate() function still cannot access vocabulary[0]. I don't see anything in the react documentation that talks about a setState() finishing or any function happening after componentDidUpdate().

Additionally during some earlier testing I tried to console.log newVocab[0] before assigning it to state in a .then string. This worked, but outputted in the console only after all my other console calls, which indicates that it is happening after the component rerender and update cycle, even though I was calling it before I called setState().

My current test code so far:

import "./Vocab.css"

 class Vocab extends React.Component {
   //HEADER HEADER HEADER

   state = {
     vocabulary: null,
     var2: null
   };

   componentDidMount(){
     //Initializing Routine Here
     this.getVocab()
   }

   componentDidUpdate(){
     console.log("Vocab update");
     console.log(this.state.vocabulary);
     console.log("Vocab[0] update");
     console.log(this.state.vocabulary[0]);
     ///Outputs the vocabulary array, and then 'undefined'
   }

   getVocab = () => {
     var newVocab = [];
     if(this.state.vocabulary == null){
       newVocab = newVocab;
     }
     else{
       newVocab = this.state.vocabulary;
     }
     this.props.HandleFetch("GET", this.props.backLink + "vocab")
      .then(r => JSON.parse(r))
      .then(data => newVocab.push(data[0]))
      .then(this.setState({vocabulary:newVocab}))
   }

   logVocab = () => {
     console.log("Vocab");
     console.log(this.state.vocabulary);
     console.log("Vocab[0]");
     console.log(this.state.vocabulary[0]);
     ///Outputs the vocabulary array, and then the dictionary at index[0].
   }

   render() {
     var display = "X";
     if(this.state.vocabulary == null){
       display = "Y";
       console.log("Y");
     }
     else{
       console.log("Vocab Z");
       console.log(this.state.vocabulary);
       console.log("Vocab[0] Z");
       console.log(this.state.vocabulary[0]);
       display = "Z";
       ///Outputs the vocabulary array, and then 'undefined'
     }
     return (
       <>
       <h1>Test</h1>
       {this.state.vocabulary ? this.state.vocabulary[0] : ""}
       <button onClick = {this.logVocab}> CLICK </button>
       </>
     );
   }
 }
 export default Vocab;
2
  • can you log your r from this.props.HandleFetch("GET", this.props.backLink + "vocab") .then(r => JSON.parse(r)) please Commented Jan 28, 2021 at 10:10
  • @antoineso Sure thing! It's [{"Word": "Test", "Mnem": "", "LastStudyDate": "2021-01-27", "SourceLearned": "TestSource", "IsCard": true, "IPA": "TestIPA", "PoS": "TestPO", "Definition": "TestDef", "Source": "TestDict", "Language": "TestLang"}] Commented Jan 28, 2021 at 10:18

2 Answers 2

1

Update: I was able to track down the problem.

The issue was with the .then string. If you use .then but do not pass an argument to the code you want to execute, it looks like the .then portion will just be omitted and it will run without waiting, which is what was causing things to run out of order.

I believe the relevant line from the documentation (here) is

If one or both arguments are omitted or are provided non-functions, then then will be missing the handler(s), but will not generate any errors. If the Promise that then is called on adopts a state (fulfillment or rejection) for which then has no handler, the returned promise adopts the final state of the original Promise on which then was called.

My updated, and working, getVocab() function:

     var newVocab = [];
     if(this.state.vocabulary == null){
       newVocab = newVocab;
     }
     else{
       newVocab = this.state.vocabulary;
     }
     this.props.HandleFetch("GET", this.props.backLink + "vocab")
      .then(r => JSON.parse(r))
      .then(data => {newVocab.push(data[0]);
                     this.setState({vocabulary:newVocab})}) 
       //Outputs correctly due to 'data' argument

   }

Thank you to everyone who commented or helped me with this.

Sign up to request clarification or add additional context in comments.

Comments

0

The problem is the state.vocabulary is still null when you're trying to log out vocabulary[0]. Since this.props.HandleFetch is an async operation, you need to wait until this.state.vocabulary is set, like this:

render() {
  const { vocabulary } = this.state;
  if (!vocabulary) {
    // render nothing if vocabulary is null
    return null;
  }

  // This will log out the first element of vocabulary instead of undefined
  console.log(this.state.vocabulary[0]);

  return (
    ...
  );
}

UPD: You also have incorrect syntax in getVocab method:

      .then(this.setState({vocabulary:newVocab}))

What it does is immediately assign vocabulary to the newVocab variable which is an empty array at the moment of execution. Then, when this.props.HandleFetch gets fulfilled, newVocab is filled with response data.

You can fix this like the following:

   this.props.HandleFetch("GET", this.props.backLink + "vocab")
      .then(r => JSON.parse(r))
      .then(data => this.setState({ vocabulary: data[0] }))

4 Comments

I guess what I don't understand is how can vocabulary[0] be null when vocabulary itself returns an array? In my render() function I do a nullcheck in the if/else tree and it outputs the array itself fine, but not the index.
Updated my answer, please check it out
I checked it out, I feel like doing it that way would limit my ability to update or add to the vocabulary list. Instead of taking what I have in vocabulary now and making a copy (as I do currently), adding to the copy, then assigning it back into state I'd be taking the direct response from the backend and assigning it to state, but wiping out any previous state I'd had there. Additionally I have the setState() function in a .then line of logic, which I had hoped would address the issue of assigning the state before actually getting the information from the server. Is this usage wrong?
Update: I applied this fix to test it and still have the same error. The array is there, but the index of the array at 0 is undefined.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.