I have recently started working on react.js, while creating the login page I have used setstate method to set the value of userEmail to text box.
I have created a method which checks the validity of email address and I am calling it every time when user enters a new letter.
handleChangeInEmail(event) {
var value = event.target.value;
console.log("change in email value" + value);
if(validateEmailAddress(value) == true) {
this.setState(function() {
return {
showInvalidEmailError : false,
userEmailForLogin: value,
}
});
} else {
this.setState(function() {
return {
showInvalidEmailError : true,
userEmailForLogin: value
}
});
}
This method and userEmailForLogin state is passed in render method as
<EmailLoginPage
userEmailForLogin = {this.state.userEmailForLogin}
onHandleChangeInEmail= {this.handleChangeInEmail}
/>
I am using the method to validate the email address and the method is
validateEmailAddress : function(emailForLogin) {
if (/^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(emailForLogin)) {
return true;
}
return false;
},
I am using this method and state in render of EmailLoginPage as <input type="text" name="" placeholder="Enter Email" className="email-input-txt" onChange={props.onHandleChangeInEmail} value = {props.userEmailForLogin}/>
This is working fine in normal case , but when I try to input a large email addess say yjgykgkykhhkuhkjhgkghjkhgkjhghjkghjghghkghbghbg#gmail.com, it crashes
IMO the frequent change in state is causing this but I couldn't understand what should be done to get rid of this.
I think issue is with the regex only, i tried with other and it's working properly.
Instead of writing the if/else inside change function simply you are write it like this:
change(event) {
var value = event.target.value;
this.setState({
showInvalidEmailError : this.validateEmailAddress(value),
value: value,
});
}
Copied the regex from this answer: How to validate email address in JavaScript?
Check the working solution:
class App extends React.Component {
constructor(){
super();
this.state = {
value: '',
showInvalidEmailError: false
}
this.change = this.change.bind(this);
}
change(event) {
var value = event.target.value;
this.setState(function() {
return {
showInvalidEmailError : this.validateEmailAddress(value),
value: value,
}
});
}
validateEmailAddress(emailForLogin) {
var regex = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if(regex.test(emailForLogin)){
return true;
}
return false;
}
render() {
return(
<div>
<input value={this.state.value} onChange={this.change}/>
<br/>
valid email: {this.state.showInvalidEmailError + ''}
</div>
);
}
}
ReactDOM.render(
<App/>,
document.getElementById("app")
);
<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>
<div id='app'/>
You could use Lodash's debounce function so that the check function is not called unless the user stops typing for x amount of time (300ms in my scenario below).
_this.debounceCheck = debounce((value) => {
if(validateEmailAddress(value)) {
this.setState(function() {
return {
showInvalidEmailError : false,
userEmailForLogin: value,
}
});
} else {
this.setState(function() {
return {
showInvalidEmailError : true,
userEmailForLogin: value
}
});
}
}, 300)
handleChangeInEmail(event) {
_this.debounce(event.target.value)
}
A solution using debounce. This way multiple setState can be reduced.
DEMO: https://jsfiddle.net/vedp/kp04015o/6/
class Email extends React.Component {
constructor (props) {
super(props)
this.state = { email: "" }
}
handleChange = debounce((e) => {
this.setState({ email: e.target.value })
}, 1000)
render() {
return (
<div className="widget">
<p>{this.state.email}</p>
<input onChange={this.handleChange} />
</div>
)
}
}
React.render(<Email/>, document.getElementById('container'));
function debounce(callback, wait, context = this) {
let timeout = null
let callbackArgs = null
const later = () => callback.apply(context, callbackArgs)
return function() {
callbackArgs = arguments
clearTimeout(timeout)
timeout = setTimeout(later, wait)
}
}
Related
I'm working on a component that should be able to:
Search by input - Using the input field a function will be called after the onBlur event got triggered. After the onBlur event the startSearch() method will run.
Filter by a selected genre - From an other component the user can select a genre from a list with genres. After the onClick event the startFilter() method will run.
GOOD NEWS:
I got the 2 functions above working.
BAD NEWS:
The above 2 functions don't work correct. Please see the code underneath. The 2 calls underneath work, but only if I comment one of the 2 out. I tried to tweak the startSearch() method in various ways, but I just keep walking to a big fat wall.
//////Searching works
//////this.filter(this.state.searchInput);
//Filtering works
this.startFilter(this.state.searchInput);
QUESTION
How can I get the filter/search method working?. Unfortunately simply putting them in an if/else is not the solution (see comments in the code).
import { Component } from 'preact';
import listData from '../../assets/data.json';
import { Link } from 'preact-router/match';
import style from './style';
export default class List extends Component {
state = {
selectedStreamUrl: "",
searchInput: "",
showDeleteButton: false,
searchByGenre: false,
list: [],
}
startFilter(input, filterByGenre) {
this.setState({
searchByGenre: true,
searchInput: input,
showDeleteButton: true
});
alert("startFilter ")
console.log(this.state.searchByGenre)
/////////---------------------------------
document.getElementById("searchField").disabled = false;
document.getElementById('searchField').value = input
document.getElementById('searchField').focus()
// document.getElementById('searchField').blur()
document.getElementById("searchField").disabled = true;
console.log(input)
this.filter(input);
}
//search
startSearch(input) {
alert("startSearch ")
console.log(this.state.searchByGenre)
//komt uit render()
if (!this.state.searchByGenre) {
//check for input
this.setState({
searchInput: input.target.value,
showDeleteButton: true,
})
//Searching works
//this.filter(this.state.searchInput);
//Filtering works
this.startFilter(this.state.searchInput);
// DOESNT WORK:
// if (this.state.searchInput != "") {
// this.filter(this.state.searchInput);
// } else {
// this.startFilter(this.state.searchInput);
// }
}
}
setAllLists(allLists) {
console.log("setAllLists")
console.log(this.state.searchByGenre)
this.setState({ list: allLists })
//document.body.style.backgroundColor = "red";
}
filter(input) {
let corresondingGenre = [];
let filteredLists = listData.filter(
(item1) => {
var test;
if (this.state.searchByGenre) {
alert("--this.state.searchByGenre")
//filterByGenre
//& item1.properties.genre == input
for (var i = 0; i < item1.properties.genre.length; i++) {
if (item1.properties.genre[i].includes(input)) {
corresondingGenre.push(item1);
test = item1.properties.genre[i].indexOf(input) !== -1;
return test;
}
this.setState({ list: corresondingGenre })
}
} else {
//searchByTitle
alert("--default")
test = item1.title.indexOf(input.charAt(0).toUpperCase()) !== -1;
}
return test;
})
console.log("filterdLists:")
console.log(filteredLists)
console.log("corresondingGenre:")
console.log(corresondingGenre)
//alert(JSON.stringify(filteredLists))
this.setState({ list: filteredLists })
}
removeInput() {
console.log("removeInput ")
console.log(this.state.searchByGenre)
this.setState({ searchInput: "", showDeleteButton: false, searchByGenre: false })
document.getElementById("searchField").disabled = false;
this.filter(this.state.searchInput)
}
render() {
//alle 's komen in deze array, zodat ze gefilterd kunnen worden OBV title.
if (this.state.list === undefined || this.state.list.length == 0 && this.state.searchInput == "") {
//init list
console.log("render ")
console.log(this.state.searchByGenre)
this.filter(this.state.searchInput)
}
return (
<div class={style.list_container}>
<input class={style.searchBar} type="text" id="searchField" placeholder={this.state.searchInput} onBlur={this.startSearch.bind(this)} ></input>
{
this.state.searchByGenre ?
<h1>ja</h1>
:
<h1>nee</h1>
}
{
this.state.showDeleteButton ?
<button class={style.deleteButton} onClick={() => this.removeInput()}>Remove</button>
: null
}
{
this.state.list.map((item, index) => {
return <div>
<p>{item.title}</p>
</div>
})
}
</div>
);
}
}
SetState is an async operation that takes a callback function. I suspect that your second function runs before the first SetState is finished.
Also, you are modifying the DOM yourself. You need to let React do that for you just by modifying state. I don't have time to write up an example now, but hopefully this helps in the meantime.
can you modify your search func,
//search
startSearch(input) {
const { value } = input.target
const { searchInput } = this.state
if (!this.state.searchByGenre) {
this.setState(prevState => ({
searchInput: prevState.searchInput = value,
showDeleteButton: prevState.showDeleteButton = true,
}))
JSON.stringify(value) !== '' ? this.filter(value) : this.startFilter(searchInput)
}
}
I've created this method that gets the state of the calculator input and checks if its empty or not. I need help with two things:
What's the cleanest way to add here a validation to check if each input is also a number and outputs and error "Input must be a number"
Currently I have one error message that fires whether all the inputs are present or not, where what I want is for it to validate each input separately and fire an error under each one. How do I do it but still keep this function concise?
constructor(props) {
super(props);
this.state = {
price: 0,
downP: 0,
term: 0,
interest: 0,
error: ''
};
}
handleValidation = () => {
const {
price,
downP,
loan,
interest,
} = this.state;
let error = '';
let formIsValid = true;
if(!price ||
!downP ||
!loan ||
!interest){
formIsValid = false;
error = "Input fields cannot be empty";
}
this.setState({error: error});
return formIsValid;
}
And then this is the error message
<span style={{color: "red"}}>{this.state.error}</span>
If you want to keep your error messages separate I would recommend to reorganize your state.
So scalable solution (you may add more controls by just adding them to state) may look like:
class NumberControlsWithErrorMessage extends React.Component {
constructor(props) {
super(props);
this.state = {
inputs: [
{ name: 'price', value: 0, error: ''},
{ name: 'downP', value: 0, error: '' },
{ name: 'term', value: 0, error: '' },
{ name: 'interest', value: 0, error: '' }
]
};
}
handleInputChange = (idx, event) => {
const target = event.target;
const name = target.name;
let error = '';
if (isNaN(target.value)) {
error = `${name} field can only be number`
}
if (!target.value) {
error = `${name} field cannot be empty`
}
this.state.inputs[idx] = {
...this.state.inputs[idx],
value: target.value,
error
}
this.setState({
inputs: [...this.state.inputs]
});
}
render() {
return (
<form>
{this.state.inputs.map((input, idx) => (
<div>
<label htmlFor="">{input.name}</label>
<input type="text" value={input.value} onChange={(e) => this.handleInputChange(idx, e)}/>
{input.error && <span>{input.error}</span> }
</div>
))}
</form>
);
}
}
Working example
Also if you are building a complex form, you may want to try some React solution for forms, where all the mechanism for listening to events, state updates, validatoin are already handled for you. Like reactive-mobx-form
A straightforward way of handling multiple objects needing validation is to store an errors object in your state that has a property for each input field. Then you conditionally render the error underneath each input field depending on whether or not it has an error. Here is a very basic example:
class Calculator extends React.Component {
constructor(props) {
super(props);
this.state = {
price: 0, downP: 0, term: 0, interest: 0,
errors: { price: '', downP: '', term: '', interest: '' }
};
}
handleValidation = () => {
const { price, downP, loan, interest } = this.state;
let errors = { price: '', downP: '', term: '', interest: '' };
if (!price) {
errors.price = 'Price is required';
} else if (isNaN(price)) {
errors.price = 'Price must be a number';
}
if (!downP) {
errors.downP = 'Down Payment is required';
}
// Rest of validation conditions go here...
this.setState({ errors });
}
render() {
const { errors } = this.state;
return (
<form>
<input name="price" value={this.state.price} onChange={this.handleChange} />
{errors.price != '' && <span style={{color: "red"}}>{this.state.errors.price}</span>}
<input name="downP" value={this.state.downP} onChange={this.handleChange} />
{errors.downP != '' && <span style={{color: "red"}}>{this.state.errors.downP}</span>}
{/** Rest of components go here */}
</form>
);
}
}
You can choose whether or not to run validation once the form submits or on every keypress and that will affect how when those messages appear and disappear, but this should give you an idea on how you would manage error messages specific to each input field.
You can do this:
handleValidation() {
const { price, downP,loan, interest} = this.state;
// only each block with generate error
if (!price || isNaN(price)) {
this.setState({ error: 'price is not valid' });
} else if (!downP || isNaN(downP)) {
this.setState({ error: 'downP is not valid' });
} else {
this.setState({error: ""})
// submit code here
}
}
Note: you dont need to return anything. It will update the error state and only submit the form if it goes into no error (else{}) part
and for render() part add this:
{(this.state.error !== '')
? <span style={{color: "red"}}>{this.state.error}</span>
: ''
}
If you want validation msg on each add errPrice, errDownP and so on to the state, and check for them in render like (this.state.errPrice!== '') {} and so on.
One solution assuming you want a one size fits all error message only checking if it was a number or not would be to put them into an array and set error if the input is not a number.
const inputs = [ price, downP, loan, interest ]
inputs.map(input => {
if (!input || isNaN(input)){
error = "Input must be a number"
formIsValid = false
}
}
this.setState({error})
Something like that maybe.
I am attempting to validate an input manually by passing up the chain the input of a text field. Some magic is supposed to happen whereby the following conditions are checked:
if input text matches that already held in an array - error = "already exists" & the text isn't added to the list
if input text is blank - error = "no text input" & the text isn't added to the list
if input text is not blank and does not already exist - run another method to add text to the list
The error is set to null by default
Currently in the input.js file, the {this.props.renderError} line causes an "underfined" output in the console before anything happens. I understand why this occurs, but I wondered if there was any way to stop it?
Functionality-wise: I can get the error message to output, however this appears to run after the text is already placed in the list of tasks...
Checkout the sandbox for this code
App.js (parent)
const tasks = [
{ name: 'task1', isComplete: false },
{ name: 'task2', isComplete: true },
{ name: 'task3', isComplete: false },
]
class App extends React.Component {
constructor() {
super();
this.state = {
error: null,
}
}
render() {
return (
<div>
<Input
createTask={this.createTask.bind(this)}
renderError={this.renderError.bind(this)}
taskList={this.state.tasks}
throwError={this.throwError.bind(this)}
/>
</div>
)
}
createTask(task, errorMsg) {
this.throwError(errorMsg);
if (this.state.error) {
return;
} else {
this.setState((prevState) => {
prevState.tasks.push({ name: task, isComplete: false });
return {
tasks: prevState.tasks
}
})
}
}
throwError(errorMsg) {
if (errorMsg) {
this.setState((prevState) => {
prevState.error = errorMsg;
return {
error: prevState.error
}
})
}
return;
}
renderError() {
if (this.state.error) {
return <div style={{ color: 'red' }}>{this.state.error}</div>
}
}
Input.js (child)
render() {
return (
<form ref="inputForm" onSubmit={this.handleCreate.bind(this)}>
<TextField placeholder="Input.js"/>
<Button type="submit">Click me</Button>
{this.props.renderError()}
</form>
)
}
validateInput(taskName) {
if (!taskName) {
return '*No task entered';
} else if (this.props.taskList.find(todo => todo.name.toLowerCase() === taskName.toLowerCase())) {
return '*Task already exists'
} else {
return null;
}
}
handleCreate(event) {
event.preventDefault();
// Determine task entered
var newTask = this.refs.inputForm[0].value;
// Constant for error message returned
const validInput = this.validateInput(newTask);
// If error message produced - trigger error to be shown & end
if (newTask) {
this.props.createTask(newTask, validInput);
this.refs.inputForm.reset();
}
}
Update
I have since found that I can make this work if I move the renderError and throwError methods to input.js and also transfer across the state property error.
I'm building a component which proceeds according to the selections of the users. I have completed it successfully but facing some issues when trying to implement a back button to go back.
My code is like follows.
class ReportMainCat extends Component {
constructor(props) {
super(props);
this.state = {
postType: null,
}
this.changeType = this.changeType.bind(this);
this.report_next = this.report_next.bind(this);
};
report_next() {
if (this.state.postType == null) {
return <ReportFirst changeType={this.changeType}/>
}
else if (this.state.postType === 'sexual') {
return <ReportXContent changeType={this.changeType}/>
} else if (this.state.postType === 'selfharm') {
return <ReportThreatContent changeType={this.changeType}/>
}
}
changeType = (postType) => {
this.setState({postType})
this.setState({
showMainReportCats: false,
})
}
render() {
return (
<div className="top_of_overlay">
<div className="section_container text_align_center padding_10px">
<a className="">Report Category</a>
{this.report_next()}
</div>
</div>
)
}
}
I'm binding the postType value as follows.
class ReportXContent extends Component {
constructor(props) {
super(props);
this.state = {
postType: '',
}
};
textType(postType) {
this.props.changeType(postType);
}
render() {
return (
<div className="text_align_left">
<div>
<div className="width_100 margin_bottom10px">
<input type="checkbox" ref="nudity" onClick={this.textType.bind(this,'nudity')}/>
<a>Nudity or Pornography</a>
</div>
<div className="width_100 margin_bottom10px">
<input type="checkbox" ref="minor" onClick={this.textType.bind(this,'minor')}/>
<a>Includes Minors</a>
</div>
</div>
<ReportButtons/>
</div>
)
}
}
My back button
<div>
<button className="float_right margin_left5px" onClick={this.props.back_report}>Back</button>
</div>
So basically what i'm trying to do is this.
Ex: If the user selects postType as sexual it will return the ReportXContent component. How can i return to the first page when the user clicks the back button.
Thanks.
You could implement the back button click handler like this in the ReportMainCat component:
handleBackClick() {
this.setState({ postType: null });
}
, and that would show the ReportFirst view again.
If you don't want the first view, but the last view, simply change your changeType implementation to save lastPostType to state like this:
changeType = (postType) => {
this.setState({
lastPostType: this.state.postType,
postType,
showMainReportCats: false,
});
}
Edit
If you want full history of changes - let's say if you want to implement a full back button history - you can simply rename lastPostType to postTypeHistory and implement it like a stack (like the browser history is):
changeType = (postType) => {
this.setState({
postTypeHistory: [...this.state.postTypeHistory, this.state.postType],
postType,
showMainReportCats: false,
});
}
handleBackClick() {
const { postTypeHistory } = this.state;
const postType = postTypeHistory.pop();
this.setState({
postType,
postTypeHistory,
});
}
For learning purpose I am writing a small app with a wizard in ReactJS.
I have created some components for the wizard as follow;
ProgressBar
Wizard
WizardStepOne
WizardStepOneForm
WizardStepTwo
WizardStepTwoForm
In the wizard component I have included my PorgressBar compnent for showing progression and created a switch statement to determine the oneClick value of the button included in each 'StepForm' to get a value and showing the next 'WizardStepTwoForm' component.
This all works well and does exactly what I expect but I am facing one problem. I don't want the user is able to get the next 'WizardStepTwoForm' form before I have validated the 'WizardStepOneForm'. So some how I should return a status to my parent component to determine if the user can click to the next state. Or, I disable the button by a state till a validation has been done but in this case the user won't be able to click on the button for an validation of the form.
During the submission of the form I want to send the data to the API, the dispatch is working but I am just wondering how to work it out right so my switch statement in the parent(Wizard) will only be fired if the form is valid.
Wizard
//.. imports
class Wizard extends React.Component {
constructor(props) {
super(props);
this.state = {
step : 'stepOne',
progression : '0%'
};
this.handleFormSubmit = this.handleFormSubmit.bind(this);
}
componentDidMount() {
const wizard = JSON.parse(localStorage.getItem('wizard'));
if (wizard !== null) {
this.setState({
step: wizard.step,
progression: wizard.progression
});
}
}
componentDidUpdate() {
localStorage.setItem('wizard', JSON.stringify({
step : this.state.step,
progression : this.state.progression
}));
}
handleFormSubmit(e) {
switch (e.target.value) {
case 'stepOne' :
this.setState({
step : 'stepOne',
progression : '0%',
});
break;
case 'stepTwo' :
this.setState({
step : 'stepTwo',
progression : '50%'
});
break;
}
}
/**
*
* Render
* #return {JSX}
*/
render() {
const { step, progression } = this.state;
switch (step) {
case 'stepOne' :
return (
<div>
<Header />
<WizardProgressBar progression={progression} stepOne="active" stepTwo="" />
<WizardStepOne handleFormSubmit={this.handleFormSubmit} />
<Footer/>
</div>
);
break;
case 'stepTwo' :
return (
<div>
<Header />
<WizardProgressBar progression={progression} stepOne="done" stepTwo="active" />
<WizardStepTwo handleFormSubmit={this.handleFormSubmit} />
<Footer/>
</div>
);
break;
}
}
}
export default Wizard;
WizardStepOne
export default class WizardStepOne extends React.Component {
constructor(props) {
super(props);
}
/**
*
* Render
* #return {XML}
*/
render() {
return(
<div className="step-one">
<h1>Step 1</h1>
<WizardStepOneForm handleFormSubmit={this.props.handleFormSubmit} />
</div>
);
}
}
WizardStepForm
//... imports
#connect((store) => {
return {
};
})
export default class WizardStepOneForm extends React.Component {
constructor(props) {
super(props);
this.state = {
formData : {
firstName : '',
lastName : '',
},
formErrors : {
firstName : true,
lastName : true,
},
formErrorMessages : {
firstName : 'some validation message',
lastName : 'some validation message',
},
formButtonEnabled : true,
}
this.handleSubmit = this.handleSubmit.bind(this);
this.handleFirstNameChange = this.handleFirstNameChange.bind(this);
this.handleLastNameChange = this.handleLastNameChange.bind(this);
}
componentDidMount() {
const stepOne = JSON.parse(localStorage.getItem('stepOne'));
if (stepOne !== null) {
this.setState({
formData : stepOne.formData,
formErrors : stepOne.formErrors,
formErrorMessages : stepOne.formErrorMessages,
});
}
}
handleFirstNameChange(e) {
let formData = this.state.formData;
let formErrors = this.state.formErrors;
let formErrorMessages = this.state.formErrorMessages;
formData.firstName = e.target.value;
if (!e.target.value) {
formErrors.firstName = true;
} else {
formErrors.firstName = false;
}
this.setState({ formData : formData, formErrors : formErrors, formErrorMessages : formErrorMessages });
localStorage.setItem('stepOne', JSON.stringify({ formData : formData, formErrors : formErrors, formErrorMessages : formErrorMessages }));
}
handleLastNameChange(e) {
let formData = this.state.formData;
let formErrors = this.state.formErrors;
let formErrorMessages = this.state.formErrorMessages;
formData.lastName = e.target.value;
if (!e.target.value) {
formErrors.lastName = true;
} else {
formErrors.lastName = false;
}
this.setState({ formData : formData, formErrors : formErrors, formErrorMessages : formErrorMessages });
localStorage.setItem('stepOne', JSON.stringify({ formData : formData, formErrors : formErrors, formErrorMessages : formErrorMessages }));
}
handleSubmitButton() {
}
handleSubmit(e) {
e.preventDefault();
this.props.dispatch(addUser(this.state.formData));
}
/**
*
* Render
* #return {XML}
*/
render() {
const firstNameError = this.state.formErrors.firstName ? 'error' : '';
const lastNameError = this.state.formErrors.lastName ? 'error' : '';
return(
<form className="step-one-form">
<div className="form-group right">
<div className="form-group__form-row">
<p className={classnames('col-2', firstNameError)}>
<label htmlFor="first_name">First name:</label>
<input type="text" id="firstName" name="fist_name" autoComplete="off" onChange={this.handleFirstNameChange} value={this.state.formData.firstName} />
{ firstNameError ? <FormElementErrorMessage message={this.state.formErrorMessages.firstName} /> : '' }
</p>
<p className={classnames('col-2', lastNameError)}>
<label htmlFor="last_name">Last name:</label>
<input type="text" id="lastName" name="last_name" autoComplete="off" onChange={this.handleLastNameChange} value={this.state.formData.lastName} />
{ lastNameError ? <FormElementErrorMessage message={this.state.formErrorMessages.lastName} /> : '' }
</p>
</div>
</div>
<button disabled={this.state.formButtonEnabled} onClick={this.props.handleFormSubmit} value="stepTwo">Next step</button>
</form>
);
}
}
So, I found out it's probably more easy as I thought.
The form submit handler comes in the child component where the form is created. So we don't need to push it downwards from the parent as a prop.
As we will fire an action via the dispatcher when the form 'stepOne' is valid, we change the given state by the reducer. The store receives this state change, which has been appended via connect and the provider at the root of our app, the parent component will receive this state change and we can fire up the next wizard screen.