I'm trying to create a basic multi-stage web form in Javascript. I wanted to accomplish this kind of like the forms section of Full Stack Open, by creating a collection (an array) of questions, then displaying them as labels on my web page, filtered so that only the appropriate ones appeared at certain times. For example, when you first visit the page, it would say "The next few questions will assess your budget situation", then after pressing start - it would transition to the first question, then pressing next, to the second, and so on.
I thought I accomplished this the correct way, by displaying the filtered collection (phase is initialized to 0 outside of the app):
const questionPhase = () => {
if (phase < 3){
phase = phase + 1;
}
else if(phase == 3){
phase = 0;
addToBudget(attribute);
}
console.log(phase);
}
return (
<div>
<h3> Please answer some questions to start</h3>
<ul>
{questions.filter(question => question.phase === phase).map(question =>
{ < label > {question.script}
<input type="text"
question = {question.attribute}
value={question.attribute}
onChange={handleInput}
/>
</label>})}
</ul>
<button onClick={questionPhase}> {phase === 2 ? 'Next' : 'Submit'} </button>
</div>
)
I've done some logging and determined that phase actually is changing every time I press the button at the bottom. But what doesn't happen, is either the questions (and labels) displaying, or the lettering on the buttons changing.
I'm not sure what I've done wrong? I'm certain there's some subtle aspect of the control flow I've missed but I don't know what - I figured that, as explained in FSO, the app is continually being run through every time there's a change by pressing a button or something, which should be the event created by the button press.
Thank you in advance for any help
appendix: here is the questionsList class (from which questions is imported) and the event handler:
import React from 'react'
const questions = [
{
phase: 1 ,
script: 'What is your monthly income?',
attribute: "income"
},
{
phase: 2,
script: 'what are you monthly expenses?',
attribute: "expenses"
}
]
export default questions
const handleInput = (event) => {
const value = event.target.value;
console.log('valued!')
setAttribute({
...attribute,
[event.target.question]: value
})
}
The only thing that will trigger a re-render in React is a change in state, so if a variable's change should cause a re-render, you should stick it in state.
You can change your questionPhase component to a class or function (same thing), and then in the constructor, define
this.state = {phase};
Then this.state.phase will equal whatever phase was when the component instantiated. You'll want to change the rest of the component to use that variable. The correct way to change its value and trigger a re-render is with a call to setState.
That's the way I would do it; although, it would be easier to just call forceUpdate. That will make react re-render. It's not the react way though. The entire purpose of react is to strongly tie the UI to the underlying state, so I wouldn't recommend using forceUpdate.