This might be convoluted, so bear with me. I'm just beginning to learn React/Redux and managing state. I want to build a small app which does not use routing which looks like this:
Not unlike an install wizard.
A user would be prompted to answer a question on each page, then press a button to advance forward or backwards. At the end, the user will see the list of their answers.
I imagine the state would look something like this:
{
steps: [
{ prompt: 'Question in step 1', answer: null },
{ prompt: 'Question in step 2', answer: null },
{ prompt: 'Question in step 3', answer: null }
],
currentStepNumber: 0,
currentStepData: { prompt: 'Question in step 1', answer: null }
}
Advancing forwards/backwards should increment/decrement currentStepNumber
while currentStepData
should show, say steps[0]
. Additionally, responses should be saved while navigating.
Finally, all results should be visible on the final "page."
I'm really scratching my head how to do this without routing by simply passing props between components and containers.
The state object above is an attempt which looks like it might work but I feel like I'm not getting it.
Specific questions:
How to show/hide components upon "navigation" without routes?
You should use what is known as conditional rendering. In your case the components could be implemented like this without the router:
const Step1 = ({prompt, onNextStep}) => (
<div>
<h1>{prompt}</h1>
<button type="button" className="btn btn-info" onClick={onNextStep}>Next</button>
</div>
);
const Step2 = ({prompt, onNextStep, onPrevStep}) => (
<div>
<h1>{prompt}</h1>
<button type="button" className="btn btn-info" onClick={onPrevStep}>Prev</button>
<button type="button" className="btn btn-info" onClick={onNextStep}>Next</button>
</div>
);
const Step3 = ({prompt, onPrevStep, onSubmit}) => (
<div>
<h1>{prompt}</h1>
<button type="button" className="btn btn-info" onClick={onPrevStep}>Prev</button>
<button type="button" className="btn btn-info" onClick={onSubmit}>Submit</button>
</div>
);
const Results = ({prompt, children, onReset}) => (
<div>
<h1>{prompt}</h1>
{children}
<button type="button" className="btn btn-info" onClick={onReset}>Reset</button>
</div>
);
class App extends React.Component {
state = {
step: 1,
steps: [
{
prompt: 'Prompt 1',
answer: ''
},
{
prompt: 'Prompt 2',
answer: ''
},
{
prompt: 'Prompt 3',
answer: ''
},
],
};
onNextStep = () => this.setState({
step: this.state.step + 1
});
onPrevStep = () => this.setState({
step: this.state.step - 1
});
onSubmit = () => this.setState({
step: null
});
onReset = () => this.setState({
step: 1
});
render() {
const { step, steps } = this.state;
const { prompt } = step ? steps[step-1] : '';
if (step === 1) {
return (
<Step1
prompt={prompt}
onNextStep={this.onNextStep}
/>
);
}
if (step === 2) {
return (
<Step2
prompt={prompt}
onNextStep={this.onNextStep}
onPrevStep={this.onPrevStep}
/>
);
}
if (step === 3) {
return (
<Step3
prompt={prompt}
onPrevStep={this.onPrevStep}
onSubmit={this.onSubmit}
/>
);
}
if (step === null) {
return (
<Results
prompt="These are results"
onReset={this.onReset}
>
<p>Drop results here as children</p>
</Results>
);
}
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.css" />
<div id="root"></div>
Should the result container simply be hidden the entire time until the last step is reached?
Again, following the conditional rendering best practices, you'd probably not want to 'hide', but instead render the result container if a certain condition is met. In my example, the result container is rendered when the state variable step
is equal to null
.
How do I design the state so that I know step N is the last step in the wizard and thus results container need to be displayed?
Your state design appears to be OK. You could check whether you reached the last step by simply comparing this.state.steps.length
to this.state.step
. If they are equal, you reached the last step.