2

I'm building a guessing game, where the questions and selection logic is in a component called Questions. I'm having trouble getting App to read the Questions code. I want the state in App to update based on selections in the child component.

I've been trying to reverse engineer a solution from How to pass data from child component to its parent in ReactJS? and https://malithjayaweera.com/2018/01/reactjs-passing-data-react-components/, but I'm not clear on how to apply it to my project.

App:

import React, { Component } from 'react';
import './App.css';
import Timer from "./Timer";
import Questions from "./Questions/Questions.js";
import Results from "../src/Results";

class App extends Component {

  state = {
    totalTrue: 0,
    totalFalse: 0,
  }

  componentDidMount() {
    console.log(`TotalTrue: ${this.state.totalTrue}`);
    console.log(`TotalFalse: ${this.state.totalFalse}`);
  }

  // submit button
  handleFormSubmit = event => {
    event.preventDefault();
    console.log("submit button clicked");
  };

  callbackHandlerFunction = (selectedOption) => {
    this.setState({ selectedOption });
  }

  render() {
    return (

      <div className="parallax">

      <div className="App">

      <div className="wrapper">

      <div className="headerDiv">
      <h1>Pixar Trivia!</h1>
    </div>

    <div className="timerDiv">
      <Timer />
      </div>

      <div className="questionSection">
      <Questions
    handleClickInParent={this.callbackHandlerFunction}
    />
    </div>

    <div>
    <button onClick={this.handleFormSubmit}>Submit</button>
      </div>

    {/* this.state.articles.length > 0 && ...*/}
  <div className="resultsDiv">
      <Results
    totalTrue={this.state.totalTrue}
    totalFalse={this.state.totalFalse}
    />
    </div>

    </div>

    </div>

    </div>
  );
  }
}

export default App;

Questions:

import React, { Component } from "react";
import Select from "react-select";
import "./Questions.css";

const answerChoices = [
    {
        id: 1,
        text: "1. The background image is the carpet from Sid's house in Toy Story. What movie inspired it?",
        answers: [
        {
            label: "2001: A Space Odyssey",
            value: false
        },
        {
            label: "The Shining",
            value: true
        },
        {
            label: "One Flew Over the Cuckoo's Nest",
            value: false
        },
        {
            label: "The Godfather",
            value: false
        }
        ]
},
  ---- full questions cut for space. I'm using https://github.com/JedWatson/react-select and the functionality works. ----
{
    id: 8,
    text: "8. Who was the original voice of Marlin from “Finding Nemo”?",
    answers: [
        {
            label: "Albert Brooks",
            value: false
        },
        { 
            label: "Denis Leary",
            value: false
        },
        {
            label: "Brad Garrett",
            value: false
        },
        {
            label: "William H. Macy",
            value: true
        }
        ]
    }
];

class Questions extends Component {

state = {
    answerChoices,
    selectedOption: null,
}

handleChange = (selectedOption) => {
    this.setState({ selectedOption });
    console.log(`Option selected:`, selectedOption);

    const answerValue = selectedOption.value;
    if (answerValue === true) {
        // console.log(answerValue);
        this.setState({totalTrue: this.totalTrue + 1}, () => {
            console.log(`New TotalTrue: ${this.totalTrue}`);
        });
    };
    if (answerValue === false) {
        // console.log(answerValue);
        this.setState({totalFalse: this.totalFalse + 1}, () => {
            console.log(`New TotalFalse: ${this.totalFalse}`);
        });
    };
    this.props.handleClickInParent({selectedOption});

  }

render() {
    // const { selectedOption } = this.state;

    return (

        <div className="questionsDiv">

            <ol>
                {this.state.answerChoices.map(question => {
                return (

                    <div className="individualQuestions" key={question.id}>

                        {question.text}

                        <Select
                            value={this.selectedOption}
                            onChange={this.handleChange}
                            options={question.answers}
                        />

                    </div>

                    )  
                })}
            </ol>

        </div>

    )
  }

}

export default Questions;
Anas Abu Farraj
  • 1,540
  • 4
  • 23
  • 31
Irene Rojas
  • 57
  • 3
  • 9
  • You are missing totalTrue and totalTrue in state of Question component, you are setting them in handleChange. In callback function from parent you are passing selectedOoption but there is no selectedOption property in state of parent which you are setting. – Sameer Reza Khan Dec 22 '18 at 19:07
  • I was hoping to have Questions update the state in App when handleChange was executed. Is that not possible at all? – Irene Rojas Dec 23 '18 at 02:53

2 Answers2

0

Look at this line in Question:

this.props.handleClickInParent({selectedOption});

It is passing { selectedOption } to callbackHandlerFunction in App. Now, that last function in App takes a single argument selectedOption, and proceeds to update the state using it. All together, you are updating the state in App using:

this.setState({ { selectedOption } }); // this is wrong.

To fix the issue, use this.props.handleClickInParent(selectedOption) (no curly braces) in Question instead.

Alternatively, if you ignore the above, you can also fix the issue by changing the signature of callbackHandlerFunction so that it look like this:

callbackHandlerFunction = ({ selectedOption }) => { // note the curly braces.
    this.setState({ selectedOption });
}    
nebuler
  • 1,515
  • 1
  • 9
  • 8
  • I've tried the suggestions from Sameer and you, and it's still not working. Here's the repo branch with the latest changes: https://github.com/irene-rojas/pixar-react/tree/refactorresults – Irene Rojas Dec 23 '18 at 03:09
  • @IreneRojas I saw the github repo. You are supposed to either add curly braces to `callbackHandlerFunction` signature OR remove curly braces from `this.props.handleClickInParent({selectedOption})`, not both. – nebuler Dec 23 '18 at 03:23
  • With the code as it is in the repo right now, try doing `this.props.handleClickInParent({selectedOption})` instead of `this.props.handleClickInParent(selectedOption)` in `Question`. – nebuler Dec 23 '18 at 03:27
  • I made the changes, but I get "New totalFalse: undefined" (etc) and the Results div always shows zero. I pushed the changes to the refactorresults repo. I'm still new with React and trying to wrap my head around component communications besides parent to child. – Irene Rojas Dec 23 '18 at 03:51
  • 1
    @IreneRojas Okay, but the state is being updates correctly in `App`, so that solves the original question. As for the issue you mention now, you have to set `totalTrue` and `totalFalse` initial values to 0 in `Question`'s state. Also, you have to access their values through the state; for example, `this.state.totalTrue` and `this.state.totalFalse` (as opposed to `this.totalTrue` and `this.totalFalse`). If you want more help regarding that, you can either update your question (or make a new one). That way, I can update my answer accordingly. – nebuler Dec 23 '18 at 04:05
  • You can find more information about a component's state here: https://reactjs.org/docs/state-and-lifecycle.html – nebuler Dec 23 '18 at 04:07
0

I made some changes and it is working now.

The code looks like this now.

callbackHandlerFunction = ( selectedOption ) => {
  const answerValue = selectedOption.value;
  if (answerValue === true) {
      // console.log(answerValue);
      this.setState({totalTrue: this.state.totalTrue + 1}, () => {
          console.log(`New TotalTrue: ${this.state.totalTrue}`);
      });
  };
  if (answerValue === false) {
      // console.log(answerValue);
      this.setState({totalFalse: this.state.totalFalse + 1}, () => {
          console.log(`New TotalFalse: ${this.state.totalFalse}`);
      });
  };
}  

and

handleChange = (selectedOption) => {

    this.props.handleClickInParent(selectedOption);
  }
Sameer Reza Khan
  • 474
  • 4
  • 15