Ira Ramírez
Ira Ramírez

Reputation: 160

"TypeError: this.setState(...) is undefined" when making a promise after setting state

I'm making a full stack application using the MERN stack to encrypt text through classic algorithms. On the frontend it has a form to input text, the desired algorithm and the factor(s) to use. It's supposed to render the encrypted text as it's being typed, so I have a function to handle any changes to the form - right now it's a work in progress, hence the console.logs:

import React, { Component } from 'react'
import CipherService from './../../services/ciphers.service'

import Form from 'react-bootstrap/Form'

import './InputForm.css'

class InputForm extends Component {
  constructor(props) {
    super(props)
    this.state = {
      text: '',
      cipher: 'caesar',
      factor: 13,
    }

    this.cipherService = new CipherService()
  }

  handleChange = (e) => {
    let { value, name } = e.target
    if (name === 'factor') {
      value = parseInt(value)
    }
    this.setState({ [name]: value })
      .then(() => {
        switch (this.state.cipher) {
          case 'caesar':
            this.cipherService
              .caesar(this.state)
              .then((result) => console.log(result.data.message))
              .catch((err) => console.log(err))
            break
        }
      })
      .catch((err) => console.log(err))
  }

  render() {
    return (
      <Form.Group as='section'>
        <Form.Control as='textarea' name='text' onChange={this.handleChange} />
        <Form.Text className='text-muted'>
          Please only input Latin characters without diacritics or spaces.
        </Form.Text>
        <div className='options-container'>
          <Form.Control as='select' name='cipher' onChange={this.handleChange}>
            <option value='caesar'>Caesar cipher</option>
          </Form.Control>
          <Form.Control
            as='input'
            type='number'
            name='factor'
            value={13}
            onChange={this.handleChange}
          />
        </div>
      </Form.Group>
    )
  }
}

export default InputForm

As it is right now, if the state of the form changes, the app crashes outputting this error:

TypeError: this.setState(...) is undefined
InputForm/this.handleChange
src/components/InputForm/InputForm.js:25
  22 | if (name === 'factor') {
  23 |   value = parseInt(value)
  24 | }
> 25 | this.setState({ [name]: value })
     | ^  26 |   .then(() => {
  27 |     switch (this.state.cipher) {
  28 |       case 'caesar':

I'm guessing it's a context problem, but I can't find the exact thing that is causing it. Making it so the function calls the API after it parses the this.setState, no promises, makes it work, but because setState is an asynchronous action, it passes the "previous" state to the API, with undesired results.

Upvotes: 0

Views: 65

Answers (1)

Shubham Khatri
Shubham Khatri

Reputation: 282050

setState is asynchronous but it doesn't return a promise for you to use .then on it. It however provides a callback as the second argument, that you can use

this.setState({ [name]: value }, () => {
     switch (this.state.cipher) {
           case 'caesar':
            this.cipherService
              .caesar(this.state)
              .then((result) => console.log(result.data.message))
              .catch((err) => console.log(err))
            break
        }

});

Upvotes: 2

Related Questions