Skip to content
Advertisement

State Not Getting Set As Required (React, Hooks)

I am having trouble setting up a state of my component. Kindly check the details as below

Codesandbox link – https://codesandbox.io/s/goofy-glade-z32zp

This is my App Component containing 4 sub components

import React, { useState } from "react";
import "./App.css";
import RadioType from "./components/Radio/RadioQuestion";
import CheckBox from "./components/Checkbox/CheckBox";
import SelectType from "./components/SelectBox/Selectbox";
import Slider from "./components/Slider/slider";
import data from "./components/jsonData/data.json";
const App = (props) => {
  
  const [values, setValues] = useState();

  const setAnswerState = (details) => {
    let newState = [];
    if (values !== null && values !== undefined) 
     newState = JSON.parse(JSON.stringify(values));
    if (newState === null || newState === undefined) {
      newState = [];
      newState.push(details);
    } else {
      if (
        newState.filter((x) => x.question_id === details.question_id).length ===
        0
      )
        newState.push(details);
      else {
        let indexOfFilteredObj = newState.indexOf(
          newState.filter((x) => x.question_id === details.question_id)[0]
        );
        newState[indexOfFilteredObj] = details;
      }
    }

    setValues(JSON.parse(JSON.stringify(newState)));
    console.log(values)
  };
  return (
    <div className="App">
      {JSON.stringify(values)}
      {data.map((v, i) => {
        return (
          <div key={i}>
            {v.question_type === "radio" && (
              <RadioType details={v} onChange={setAnswerState} />
            )}
            {v.question_type === "checkbox" && (
              <CheckBox details={v} onChange={setAnswerState} />
            )}
            {v.question_type === "select" && (
              <SelectType details={v} onChange={setAnswerState} />
            )}
            {v.question_type === "slider" && (
              <div style={{ marginLeft: "300px" }}>
                <Slider details={v} onChangeState={setAnswerState} />
              </div>
            )}
          </div>
        );
      })}
    </div>
  );
};

export default App;

Checkbox

import React, { useState } from "react";
const CheckBox = (props) => {
  const { details, onChange } = props;
  const [checkedValues, setCheckedValues] = useState([]);
  const setGlobalState = (value) => {
    let answer = value;
    let stateObj = {
      question_id: details.questionId,
      answers: answer,
      question_type: "checkbox",
    };
    onChange(stateObj);
  };
  return (
    <div>
      <div>{details.question}</div>
      <label>
        {details.answers === undefined
          ? null
          : details.answers.map((checkBoxAnswers, index) => {
              return (
                <div key={index}>
                  <input
                    type="checkbox"
                    name={`checkbox_${details.questionId}`}
                    value={checkBoxAnswers}
                    onChange={(e) => {
                      let currentValues = checkedValues;

                      if (e.target.checked) currentValues.push(e.target.value);
                      else {
                        const index = currentValues.indexOf(e.target.value);

                        if (index > -1) {
                          currentValues.splice(index, 1);
                        }
                      }
                      setCheckedValues(currentValues);
                      setGlobalState(currentValues);
                    }}
                  />
                  <label key={index}>{checkBoxAnswers}</label>
                </div>
              );
            })}
      </label>
      <br />
    </div>
  );
};
export default CheckBox;

Radio

import React from "react";
const RadioType = (props) => {
  const { details, onChange } = props;
  const setGlobalState = (value) => {
    let answer = [value];
    let stateObj = {
      question_id: details.questionId,
      answers: answer,
      question_type: "radio",
    };

    onChange(stateObj);
  };

  return (
    <div>
      <div>{details.question}</div>
      <label>
        {details.answers === undefined
          ? null
          : details.answers.map((radioQuestionAnswers, index) => {
              return (
                <div key={index}>
                  <input
                    type="radio"
                    name={`radio_${details.questionId}`}
                    value={radioQuestionAnswers}
                    onChange={(e) => setGlobalState(e.target.value)}
                  />
                  <label key={index}>{radioQuestionAnswers}</label>
                </div>
              );
            })}
      </label>
      <br />
    </div>
  );
};
export default RadioType;

Select

import React from "react";
const SelectType = (props) => {
  const { details, onChange } = props;
  const setGlobalState = (value) => {
    let answer = [value];
    let stateObj = {
      question_id: details.questionId,
      answers: answer,
      question_type: "select",
    };
    onChange(stateObj);
  };

  return (
    <>
        <div>{details.question}</div>
        <select
          name={`checkbox_${details.questionId}`}
          onChange={(e) => {
            setGlobalState(e.target.value);
          }}
        >
          {details.answers === undefined
            ? null
            : props.details.answers.map((selectAns, index) => {
                return (
                  <option key={index} value={selectAns}>
                    {selectAns}
                  </option>
                );
              })}
        </select>
        
    </>
  );
};
export default SelectType;

NouiSlider

import React from "react";
import Nouislider from "nouislider-react";
import "nouislider/distribute/nouislider.css";

const Slider = (props) => {
  const { details, onChangeState } = props;
  const setGlobalState = (value) => {
    let answer = [value];
    let stateObj = {
      question_id: details.questionId,
      answers: answer,
      question_type: "slider",
    };
    onChangeState(stateObj);
  };

  return (
    <>
      <Nouislider
        style={{ color: "red", width: "600px" }}
        start={[0]}
        pips={{ mode: "count", values: details.division }}
        clickablePips
        range={{
          min: details.range.min,
          max: details.range.max,
        }}
        onChange={(e) => {
          let valueOfSlider = parseFloat(e[0]);
          setGlobalState(valueOfSlider);
        }}
      />
    </>
  );
};
export default Slider;

Coming to the problem whenever I try to set state from the radio checkbox or select it does set the state and updates correctly via setAnswerState method that is passed as prop to child. Whenever I try to change the slider the setAnswerState gets values as undefined or null, so the complete state that is set by other child components is lost, I am not able to find the reason behind state lost.

Here is the sample data I used for testing

[
  {
    "question": "When was the Cilivar War?",
    "question_type": "radio",
    "answers": ["1877-1866", "1877-1872", "1877-1851", "1877-1880"],
    "questionId": "099011"
  },
  
  {
    "question": "World's Largest country by population?",
    "answers": ["China", "Canada", "United kingdom", "India"],
    "correctAns": "China",
    "question_type": "checkbox",
    "dependent_question_id": [
      {
        "question_id": "099011",
        "answers_to_match": [
          {
            "operator": "AND",
            "answers": ["1877-1866"]
          }
        ]
      }
    ],
    "questionId": "0990758"
  },
  {
    "question": "Where is the Tajmahal?",
    "answers": ["Agra", "Mumbai", "Pune", "Punjab"],
    "correctAns": "Agra",
    "question_type": "select",
    "dependent_question_id": [
      {
        "question_id": "099011",
        "answers_to_match": [
          {
            "operator": "AND",
            "answers": ["1877-1866"]
          }
        ]
      },
      {
        "question_id": "099078",
        "answers_to_match": [
          {
            "operator": "AND",
            "answers": ["Canada", "United kingdom"]
          }
        ]
      }
    ],
    "questionId": "099096"
  },
  {
    "questionId": "0002346",
    "question_type": "slider",
    "division": 5,
    "range": 
      {
        "min": 0,
        "max": 100
      }
    
  }
]

Advertisement

Answer

Just add [] in useState() as shown below:

const [values, setValues] = useState([]);

And then update the setAnswerState method to:

const setAnswerState = (details) => {
  // below statement will handle both the cases:
  // updating an existing question as well as adding in a new question
  setValues((prevValues) => [
    ...prevValues.filter((x) => x.question_id !== details.question_id),
    { ...details }
  ]);
};
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement