-1

Okay so I am working on a simple Random Quote Generator React app and have used the a class component App along with Fetch API for the same.

Every time I run the app(both in production and development mode) I am getting a TypeError: this is undefined in the console right when it goes through the this.setState() part in the fetchQuote() method.

The data is showing up in the console, when I console.log(tempText) but it is not able to enter the setState method and actually update the state variables for some reason and goes straight to the catch statement and throws the error.

I have already used the 'this' binding for that method inside the constructor. So I am not entirely sure why the error shows up. I have also tested it while using the bind statement directly in the return statement of render(), but that did not resolve the issue either.

App.js:

import "./App.css";
import React from "react";

class App extends React.Component {
  constructor(props) {
    super(props);
    // State Variables go here:
    this.state = {
      quoteText: "Initial text",
      quoteAuthor: "Initial author",
    };

    this.fetchQuote = this.fetchQuote.bind(this);
  }

  // Accessing the fetchQuote() method inside the componentDidMount() Lifecycle method
  componentDidMount() {
    this.fetchQuote();
  }


  fetchQuote() {
    let tempText = "Temptext";              // tempText  is just there for testing and changing the state value
    let tempAuthor = "Tempauthor";          // tempAuthor is just there for testing and changing the state value

    fetch("https://type.fit/api/quotes")
      .then(function (response) {
        return response.json();
      })
      .then(function (data) {
        let quoteIndex = Math.floor(Math.random() * data.length);

        const newquoteText = data[quoteIndex].text;
        const newquoteAuthor = data[quoteIndex].author;

        tempAuthor = newquoteAuthor;
        tempText = newquoteText;

        // data
        console.log(tempText);
        console.log(tempAuthor);

        // Setting the state variables here:
        // This part is not working

        this.setState({
          quoteText: tempText,
          quoteAuthor: tempAuthor,
        });


      }) // closing the .then for data
      .catch((error) => {
        console.log("There was some error here");
        console.log(error);
      });
  }

  render() {
    return (
      <div className="App">
        <div className="main-container">
          <div className="container__box">
            <div className="quote-left"></div>
            <p className="quote__paragraph">
              {this.state.quoteText}  
              {/* Setting the Quote Text here */}
            </p>
            <div className="quote-author">
              -{" "}
              <span id="author">
                {this.state.quoteAuthor}
                {/* Setting the Author here */}
              </span>
            </div>

            <div className="buttons">
              <div className="socials">
                <a className="button" id="tweet-quote" href="#">
                  <i className="fa-brands fa-twitter"></i>
                </a>
                <a className="button" id="tumblr-quote" href="#">
                  <i className="fa-brands fa-tumblr"></i>
                </a>
              </div>
              <button
                onClick={this.fetchQuote}
                className="button"
                id="new-quote"
              >
                New quote
              </button>
            </div>
          </div>

          <div className="footer">
            by{" "}
            <a href="#" className="link">
              Vidya Sagar 
            </a>
          </div>
        </div>
      </div>
    );
  }
}

export default App;

index.js:

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  // <React.StrictMode>
    <App />
  // </React.StrictMode>
);

reportWebVitals();

I have tried using a different variation for this.setState() however that didn't work either:

        this.setState((state) => ({
          quoteText: tempText,
          quoteAuthor: tempAuthor,
        }));

Screenshot of the browser: App Screenshot from Browser

How can I fix this?

  • This should help. https://typeofnan.dev/how-to-fix-undefined-this-state-in-react-class-components/ – tomleb Sep 11 '22 at 08:39
  • @tomleb I have tried all three options here but unfortunately none of them worked. I have already added the event handler(fetchQuote) binding in the constructor as you can see in the code above but I am still getting the TypeError – Vidya Sagar Sep 11 '22 at 08:45
  • @jonrsharpe I don't think so, unless I am missing something blatantly obvious here. I have tried using arrow function for the fetchQuote event handler, however the issue persists. Also, you can see that I have tried to use the bind method on the event handler. I even tried it inline inside the return statement but that did not solve the issue either and I am getting the TypeError. – Vidya Sagar Sep 11 '22 at 08:53
  • It does, you are missing something obvious: _where_ is the `this` you're having problems with? What function is it inside? Is _that_ bound? – jonrsharpe Sep 11 '22 at 09:03
  • Aah yes, you were right. I was using the 'this' inside the callback function of the .then of fetch. I tried what Aloiso had mentioned in the Answers below and i can see what I was doing wrong. – Vidya Sagar Sep 11 '22 at 09:13

2 Answers2

0

You are facing self reference inside JavaScript callback.

When you are trying use "this" inside callback of fetch the context inside is no more your class, is the callback out of bounds of your class so when you call This selector inside callback it looks to callback members. You can reference your main class properties by creating a global variable outside callback Which stores class "this" selector safe guarding your usage inside callback.

So outside fetch API if you just create: var self = this; and replace inside callback all "this" by "self" you will see desired result.

My apologies due long explain. This is a hard concept to puts out

Aloiso Junior
  • 417
  • 3
  • 10
  • Aah yes this was it. Thank you so much for your comment, this solved my issue. I think if I understood what you mentioned here I had lost the context to 'this' the moment I was inside the fetch API. I used self outside the same as a variable like you mentioned and it worked. – Vidya Sagar Sep 11 '22 at 09:08
-1

In most cases, the value of this is determined by how a function is called (runtime binding). It can't be set by assignment during execution, and it may be different each time the function is called. The bind() method can set the value of a function's this regardless of how it's called. (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this)

class App extends React.Component {
    constructor(props) {
        //...
        this.fetchQuote = this.fetchQuote.bind(this);
        this.setQuote = this.setQuote.bind(this);
    }

    setQuote(data)
    {
        //...
        this.setState({
            quoteText: tempText,
            quoteAuthor: tempAuthor,
        });
    }

    fetchQuote() {
        //...
        fetch("https://type.fit/api/quotes")
        .then(function (response) {
            return response.json();
        })
        .then(this.setQuote)
        //...
    }
}
Sadiq Salau
  • 119
  • 5