I've spent a few days going back and forth now trying to figure this one out but I'm unable to.
First off it's easiest to describe the end goal.
- In SectionQuestions component there is an add to cart button, on click it should send an object of data to the cartItems state.
- The SectionQuestions component gets its data from the QuestionsList component.
- The QuestionsList component gets it's answers from the 2 different input components
The issues I am facing:
- The questions are dynamically pulled from WordPress so the code must be dynamic as questions cant be hardcoded.
- There are multiple questions depending on what service is selected.
- When using a radio button the value was added to an object twice instead of overwriting the previous value.
- I am using ChakraUI where I don't believe you can expand on their onChange value for RadioGroups so I am unable to run code as a handler instead must use the useEffect hook
What I Think the solution is
I believe that each input should pass its answer up to the parent component. I believe the QuestionsList component should store the answer and know where the answer came from. (This is less of a challenge for the text/number field and more so an issue for radio buttons)
Let's get into some code... I apologise in advance there's a lot for the purpose of length I will display only the required sections of each file:
SectionQuestions.js
allWpService.nodes.map(service => {
return (
<div key={service.id}>
{selected === service.id ? (
<div>
<p>{service.title}</p>
<QuestionList service={service} />
</div>
) : null}
</div>
)
})
QuestionList.js
import React, { useState } from "react"
import InputRadioButton from "../InputFields/InputRadioButton"
import InputTextNumber from "../InputFields/InputTextNumber"
const QuestionList = ({ service }) => {
const [answers, setAnswers] = useState([])
const setAnswersValue = index => {
setAnswers(answers.push(index))
console.log(answers)
}
return (
<>
{service.serviceQuestions.questions.map(questions => {
return (
<div key={questions.questionTitle}>
<p>{questions.questionTitle}</p>
{questions.questionInputField.map(fields => {
if (
fields.inputType === "number" ||
fields.inputType === "text"
) {
return (
<InputTextNumber
fieldData={fields}
setAnswersValue={setAnswersValue}
/>
)
} else if (fields.inputType === "radio") {
return (
<InputRadioButton
fieldData={fields}
columns={questions.enableColumnLayout}
setAnswersValue={setAnswersValue}
/>
)
} else {
return <p>Failed to get Input Type</p>
}
})}
</div>
)
})}
</>
)
}
export default QuestionList
InputTextNumber.js
//This Input field is a number field
import React from "react"
import {
NumberInput,
NumberInputField,
NumberInputStepper,
NumberIncrementStepper,
NumberDecrementStepper,
} from "@chakra-ui/react"
import { useState } from "react"
const InputTextNumber = ({ fieldData, questionsAnswers }) => {
const [value, setValue] = useState(0)
return (
<NumberInput
size="lg"
defaultValue={0}
onChange={valueString => {
setValue(valueString)
questionsAnswers(valueString)
}}
value={value}
>
<NumberInputField />
{fieldData.inputType === "number" ? (
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
) : null}
</NumberInput>
)
}
export default InputTextNumber
InputRadioButton.js
import React, { useEffect } from "react"
import "../../css/InputRadioButton.css"
import { Radio, RadioGroup } from "@chakra-ui/react"
const InputRadioButton = props => {
const [value, setValue] = React.useState("")
const radios = props.fieldData.radioButtons
const questionId = "12342341"
function setValueProps(id, value) {
const obj = {}
obj[id] = { value: value }
props.setAnswersValue(obj)
}
useEffect(() => {
setValueProps(questionId, value)
}, [value])
return (
<RadioGroup onChange={setValue} value={value}>
<div
className={
props.fieldData.columns === true ? "flex-row" : "flex-column"
}
>
{radios.map(radio => {
return (
<div
className={`radio-container ${
value === radio.inputLabel ? "active" : ""
}`}
key={radio.inputLabel}
>
<div className="radio-wrapper" id={radio.inputImagePosition}>
{radio.inputImage && radio.inputImagePosition !== "no-image" ? (
<div className="image-wrapper">
<img
src={radio.inputImage.sourceUrl}
alt={radio.inputLabel}
/>
</div>
) : null}
{radio.inputImagePosition === "no-image" ? (
<div className="radio-button-wrapper">
<Radio
value={radio.inputLabel}
name={radio.inputLabel}
size="lg"
colorScheme="green"
/>
</div>
) : null}
<div className="radio-content">
{radio.inputImagePosition === "image-top" ? (
<div className="radio-button-wrapper">
<Radio
value={radio.inputLabel}
name={radio.inputLabel}
size="lg"
colorScheme="green"
/>
</div>
) : null}
{radio.inputIntroPosition === "above" &&
radio.inputIntro !== "none" &&
radio.inputIntro !== null ? (
<div className="radio-content-wrapper">
<p className="radio-intro">{radio.inputIntro}</p>
<p className="radio-label">{radio.inputLabel}</p>
<p className="radio-description">
{radio.inputDescription}
</p>
</div>
) : radio.inputIntroPosition === "below" &&
radio.inputIntro !== "none" &&
radio.inputIntro !== null ? (
<div className="radio-content-wrapper">
<p className="radio-label">{radio.inputLabel}</p>
<p className="radio-intro">{radio.inputIntro}</p>
<p className="radio-description">
{radio.inputDescription}
</p>
</div>
) : (
<div className="radio-content-wrapper">
<p className="radio-label">{radio.inputLabel}</p>
<p className="radio-description">
{radio.inputDescription}
</p>
</div>
)}
</div>
{radio.inputImagePosition === "image-left" ? (
<div className="radio-button-wrapper">
<Radio value={radio.inputLabel} name={radio.inputLabel} />
</div>
) : null}
</div>
</div>
)
})}
</div>
</RadioGroup>
)
}
export default InputRadioButton