0

I have a simple react flashcard app that sends data to the backend about the flashcard including the question, answer choices, and the answer that the user guessed along with the correct answer. I am trying to post the user's name that they enter into the form to the same backend route as the other data. I have successfully made the form and on submit the program alerts the user that they've entered their username and it displays the username. that works perfectly. Now I'm trying to get that value that was entered for the username and post it to the backend in the same function that I post the other data to the backend so it all gets sent together conveniently on each click. Here is my updated code for my form:

import React from "react"

export default class NameForm extends React.Component {
constructor(props) {
  super(props);
  this.state = {value: ''};   
  this.handleChange = this.handleChange.bind(this);
  this.handleSubmit = this.handleSubmit.bind(this);
  this.checkGuess = this.checkGuess.bind(this);
}

handleChange = (event) => {
    this.setState({value: event.target.value});
}

handleSubmit = (event) => {  
    alert('A name was submitted: ' + this.state.value);
    event.preventDefault();
}
checkGuess() {

}

render() {
  return (
    <form onSubmit={this.handleSubmit}>
      <label>
        Name:
        <input type="text" value={this.state.value} onChange= 
  {this.handleChange} />
      </label>
      <input type="submit" value="Submit" />
    </form>
  );
}

}

and here is the other component that builds the flashcard and posts the data to the endpoint onClick through the checkGuess function. This already works perfectly without the new username value. :

 import React, { useState, useEffect, useRef } from 'react'
 import NameForm from './NameForm'




export default function Flashcard({ flashcard }) {     // recieving 
flashcard 
prop from our mapping in flashcardlist.js, each w a unique id

const MAX_TRIES = 4
// const [incorrect, setIncorrect] = useState(incorrect)
const [guess, setGuess] = useState(0)
const [flip, setFlip] = useState(false)
const [height, setHeight] = useState('initial') //sets the state for our 
initial height to be replaced by the max height

const frontEl = useRef() // lets us have a reference from the front and 
back through every rerendering of them
const backEl = useRef()

// const callDouble = () =>{
 //   checkGuess();
  //  postData();

// }

async function postData() {

    
}



const checkGuess = (answer) => {
    try {
        console.log(this.state.value)
        let result = fetch('http://127.0.0.1:5000/post', {
            method: 'POST',
            mode: 'no-cors',
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json',

            },
            body: JSON.stringify({
                key: `${Date.now()}`,
                question: flashcard.question,
                answer: flashcard.answer,
                options: flashcard.options,
                guess: answer,
                user: this.state.value
            })
        });
    } catch(e) {
        console.log(e)
    }
    if (answer === flashcard.answer) {
        setFlip(true)
        return
    }
    if (guess + 1 === MAX_TRIES) {
        setFlip(true)
    }

    setGuess(guess + 1)
    // setIncorrect(true)
}

function setMaxHeight() {
    const frontHeight = frontEl.current.getBoundingClientRect().height 
//gives us dimensions of the rectangle but we only need the height
    const backHeight = backEl.current.getBoundingClientRect().height
    setHeight(Math.max(frontHeight, backHeight, 100)) // sets the height 
(setHeight) to the maximum height of front or back but the minimum is 
100px
}

useEffect(setMaxHeight, [flashcard.question, flashcard.answer, 
flashcard.options]) //anytime any of these change then the setMaxHeight 
will change
useEffect(() => {
    window.addEventListener('resize', setMaxHeight) //everytime we resize 
our browser, it sets the max height again
    return () => window.removeEventListener('resize', setMaxHeight) 
//removes the eventlistener when component destroys itself
  }, [])


 return (
<div 
    onClick={() => postData()}
    className={`card ${flip ? 'flip' : ''}`} // if flip is true classname 
will be card and flip, if flip isnt true it will just be card
    style={{ height: height }} //setting height to the variable height

    // onClick={() => setFlip(!flip)} // click changes it from flip to non 
flip
>
    <div className="front" ref={frontEl}>
        {flashcard.question}
        <div className='flashcard-options'>
            {flashcard.options.map(option => {
                return <div key={option} onClick={() => 
checkGuess(option)} className='flashcard-option'>{option}</div>
            })}
        </div>
    </div> 
    <div onClick={() => setFlip(!flip)} className='back' ref={backEl}>
        {flashcard.answer}        
    </div>
</div>
 )
}
// setting the front to show the question and the answers by looping 
through the options to make them each an option with a class name to style
// back shows the answer

and this is the new error:

TypeError: Cannot read properties of undefined (reading 'state')
at checkGuess (Flashcard.js:34:1)
at onClick (Flashcard.js:92:1)
sleepysanjay
  • 37
  • 1
  • 5

1 Answers1

0

Since the question has changed a bit based on new code shared by you, please remove the following line: this.checkGuess = this.checkGuess.bind(this);

Now React components work in a tree structure Root->Children

So you need to figure out whether Flashcard is the root, calling NameForm. Or the other way round.

If Flashcard calls NameForm:

  1. Call NameForm as follows while passing in the checkGuess function: <NameForm answer={flashcard.answer} checkGuess={checkGuess} />

  2. Then inside NameForm:

    handleSubmit = (event) => {
    alert('A name was submitted: ' + this.state.value); this.props.checkGuess(this.props.answer, this.state.value); event.preventDefault(); }

  3. To support this in Flashcard, change the function signature of checkGuess to:

    const checkGuess = (answer, stateValue) => { ... }

And here you use stateValue instead of this.state.value

Aneesh
  • 1,720
  • 4
  • 14
  • Ok ive added it in my constructor like this: constructor(props) { super(props); this.state = {value: ''}; this.checkGuess = this.checkGuess.bind(this); this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } – sleepysanjay May 05 '22 at 20:34
  • and now i get the error again: Uncaught TypeError: Cannot read properties of undefined (reading 'bind') at new NameForm (NameForm.js:7:1) at constructClassInstance (react-dom.development.js:12716:1) at updateClassComponent (react-dom.development.js:17425:1) at beginWork (react-dom.development.js:19073:1) at HTMLUnknownElement.callCallback (react-dom.development.js:3945:1) at Object.invokeGuardedCallbackDev (react-dom.development.js:3994:1) at invokeGuardedCallback (react-dom.development.js:4056:1) – sleepysanjay May 05 '22 at 20:34
  • @sleepysanjay keep `checkGuess` between `handleSubmit` and `render`. That is, inside that class – Aneesh May 05 '22 at 20:36
  • what do you mean by that? do you mean move the entire checkGuess function inbetween handleSubmit and render? the checkGuess function is in a different file than the 'NameForm" class, NameForm.js is it's own file. – sleepysanjay May 05 '22 at 20:47
  • Ah, I see, so basically, this.state.value is the state of NameForm, hence it can't be accessed outside, directly. You can pass the value between the components though depending upon which one is parent and which one is child. You will have to share the other component as well – Aneesh May 05 '22 at 20:50
  • ok im going to edit my question, one second. also thanks so much for your help aneesh i really appreciate it – sleepysanjay May 05 '22 at 20:57
  • just edited it so you can now see the other component – sleepysanjay May 05 '22 at 21:02
  • I see you imported `NameForm` in `Flashcard` but I don't see where it is called, can you share that as well. Or have you not called it there? – Aneesh May 05 '22 at 21:09
  • I havent called it in there – sleepysanjay May 05 '22 at 21:10
  • What i'm trying to figure out is, if the link goes like Flashcard->NameForm, or NameForm->Flashcard. Then I can share with you how to get it to work – Aneesh May 05 '22 at 21:11
  • i think it goes Flashcard->NameForm because the onclick is inside flashcard which is the function that posts the data. Or is it NameForm->Flashcard because the onSubmit is in the NameForm which assigns the value that is used in the checkGuess function in Flashcard – sleepysanjay May 05 '22 at 21:21
  • I've updated my answer. you can remove the changes you did previously, and try this out. Hope it helps – Aneesh May 05 '22 at 21:29
  • okay that got rid of the error but now when I try to console log it within that checkguess function, it says undefined in the terminal – sleepysanjay May 05 '22 at 21:40
  • If you can update the new code, we can check – Aneesh May 05 '22 at 21:42
  • ive posted a new question https://stackoverflow.com/questions/72220346/how-to-lift-state-properly-so-that-i-can-pass-my-prop-between-components – sleepysanjay May 12 '22 at 19:01