Search code examples
javascriptreactjscreate-react-app

I can't figure out how to define state and this.setState in react


I'm following a tutorial on how to post text to a django rest api server. Every tutorial says to use state and this.setState in order to update the api form and send it to the server. But after about two days of trying, I can't figure out whats wrong.

I've already tried using a constructor, setting App to a class instead of a function, and screwing around with the stuff in every tutorial I can find. I am really new to this so it's probably something obvious.

Heres my App.jsx


import React from 'react';

import './scss/sass.scss';

import './scss/customBootstrap.scss';

import 'react-bootstrap';

import axios from 'axios';

function App() {

    var fontWeight = {
        fontWeight: 'bold',
    };

    var backgroundColor = {
        backgroundColor: 'chartreuse'
    };

    function onClick() {
        console.log("Sending a GET API Call !!!");
        axios.get('http://127.0.0.1:8000/api')
        .then(res => {
            console.log(JSON.stringify(res))
        })
    };

    var state = { description: '' }

      function handleChange(e) {
        this.setState({
          [e.target.id]: e.target.value
        })
      };

      function handleSubmit(e) {
        e.preventDefault();
        console.log(this.state);
        let form_data = new FormData();

        form_data.append('description', this.state.description);
        let url = 'http://localhost:8000/api/';
        axios.post(url, form_data, {
          headers: {
            'content-type': 'multipart/form-data'
          }
        })
            .then(res => {
              console.log(res.data);
            })
            .catch(err => console.log(err))
      };

  return (


    <React.Fragment>

    <div className="container">

    <div className="row">

    </div>

    </div>


    <div className="container">

    <div className="row" style={backgroundColor}>

    <div className="col">
    <form className="search-bar">
    <input type="text" placeholder="Search"/>
    <button type="submit"></button>
    </form>

    </div>
    </div>

    </div>

    <div className="container">

    <div id="post" className="row">
    <div className="col">
    <form className="user-post">
    <textarea className="user-post" placeholder="Write a post here"></textarea>
    <button type="submit"></button>
    </form>
    </div>
    </div>

    <div id="content" className="row">
    <p className="link-text">Content will be here</p>
    </div>
//this is a get request that somehow works fine compared to the post request
    <div>
    <button type="button" onClick={onClick}>Send GET /products</button>
    </div>

//this is the tutorial text input form im trying to make work
    <form onSubmit={handleSubmit}>
    <p>
    <input type="text" placeholder='Description' id='description' value={this.state.description} onChange={handleChange} required/>
    </p>
    <input type="submit"/>
    </form>

    <div id="feedback" className="row">
    <div className="col">
    <form className="feedback" name="feedback">
    <textarea maxLength="335" className="feedback" placeholder="Write your feedback here"></textarea>
    <button type="submit"></button>
    </form>
    </div>
    </div>

    </div>


<footer>

</footer>

      </React.Fragment>
  );
}

export default App;

What should happen in the end is that it sends the inputed text to the api, but when I try to send text data from the browser the browser gives me: "TypeError: Cannot read property 'setState' of undefined".


Solution

  • This confusion has to do with the evolution of React over time. For a long time, the only way you could use state in React was by using a class component and this.setState to update it. This is why it is the preferred method in most of the tutorials you found.

    class App extends Component {
      state = {
        //some state 
      }
      handleChange = () => {
        this.setState({
          // something has changed, update state.
        })
      }
      render() {
        return (
          // return some jsx
        );
      }
    }
    

    By virtue of extending Component in your class components, you can use this.setState inside their event handlers to update state and refresh the UI. But this is not possible in a functional component as you have tried.

    But earlier this year (Feb 2019), React has introduced a way called Hooks that can be used as state in functional components. See how to use that in Using the State hook

    Instead of mixing both these patterns, you would have to either class component and this.setState or a functional component and useState.