TLDR
I'm making an multiStep form for my project that is inspired from Brad Traversy's Tutorial of making Multi-Step form in React.
So as per the basic structure of this form
I made a main Parent component called Multiform
as below
import React, { Component } from 'react'
import StepOne from './StepOne'
export class Multiform extends Component {
state = {
step:1,
CountryFlag: 'https://raw.githubusercontent.com/MeRahulAhire/country-calling-code-html/master/phone_icon.png',
CountryCode: ''
};
handleChange = (input) => (e) => {
this.setState({ [input]: e.target.value });
};
countryFlagHandler = () =>{
this.setState({CountryFlag : this.props.state.flagImg})
}
render() {
const { CountryFlag, CountryCode } = this.state;
const values = {CountryFlag, CountryCode };
switch (step) {
case 1:
return(
<StepOne
handleChange={this.handleChange}
countryFlagHandler={this.countryFlagHandler}
values={values}
/>
)
default:
return (<h1>hello React App</h1>)
};
}
}
export default Multiform
and a child component StepOne
as below
import React, { Component } from 'react'
export class StepOne extends Component {
state = {
flagImg: '',
};
render() {
const { values, handleChange, countryFlagHandler } = this.props;
const selectCountryChange = () => {
const img = document.querySelector('#img');
const select = document.querySelector('#country');
img.src = `https://flagpedia.net/data/flags/h80/${select.selectedOptions[0].dataset.countrycode.toLowerCase()}.webp`;
this.setState({
flagImg: `https://flagpedia.net/data/flags/h80/${select.selectedOptions[0].dataset.countrycode.toLowerCase()}.webp`
});
countryFlagHandler()
};
return (
<div>
<div class="image" onChange={selectCountryChange}>
<img src={values.CountryFlag} id="img"/>
</div>
<select id="country" onChange={handleChange('select')} defaultValue={values.select}>
<option data-countryCode="IN" value="91">India</option>
<option data-countryCode="US" value="1">US</option>
<option data-countryCode="GB" value="44">UK</option>
</select>
</div>
)
}
}
export default StepOne
what I'm trying to do is actually to sync and persist the data of <Select/>
and <img>
in Multiform.js
Component as typically what we see in a stepper form.
But, As in the StepOne
<img src={values.CountryFlag} id="img"/>
the img.src
is actually manipulated by the function selectCountryChange
and to keep the value of img.src
persisted I thought of creating countryFlagHandler
in Multiform
and importing it to StepOne
but when i selected any value, it gave me this error:
TypeError: Cannot read property 'flagImg' of undefined
Registration.countryFlagHandler
C:/Users/Rahul/Desktop/cfm-usersignup/src/public/form/registration.js:53
50 | this.setState({ [input]: e.target.value });
51 | };
52 | countryFlagHandler = () =>{
> 53 | this.setState({CountryFlag : this.props.state.flagImg})
| ^ 54 | }
55 |
56 |
&
selectCountryChange
C:/Users/Rahul/Desktop/cfm-usersignup/src/public/form/credential.js:31
28 | this.setState({
29 | flagImg: `https://flagpedia.net/data/flags/h80/${select.selectedOptions[0].dataset.countrycode.toLowerCase()}.webp`
30 | });
> 31 | countryFlagHandler();
| ^ 32 | };
33 | return (
34 | <div>
Can anyone please tell me how to rectify my error?
Shor answer
You're getting an error because countryFlagHandler
is not getting the value it's expected, it doesn't have access to the StepOne
component's state. You would need to pass the value as an argument to the parent component.
// flagImg will come as an argument from the child Component
countryFlagHandler = (flagImg) =>{
this.setState({ CountryFlag : flagImg })
}
const selectCountryChange = () => {
const img = document.querySelector('#img');
const select = document.querySelector('#country');
img.src = `https://flagpedia.net/data/flags/h80/${select.selectedOptions[0].dataset.countrycode.toLowerCase()}.webp`;
this.setState({
flagImg: `https://flagpedia.net/data/flags/h80/${select.selectedOptions[0].dataset.countrycode.toLowerCase()}.webp`
});
const countryFlag = `https://flagpedia.net/data/flags/h80/${select.selectedOptions[0].dataset.countrycode.toLowerCase()}.webp`;
// CountryFlag would be passed as an argument
countryFlagHandler(countryFlag);
};
Long Answer
I would recommend refactoring your code a bit and moving all the data to the parent component rather than keeping them in two different states. And also one function would be enough to handle all the data manipulation.
Parent Component Multiform
import React, { Component } from 'react'
import StepOne from './StepOne'
export class Multiform extends Component {
state = {
step: 1,
CountryFlag: 'https://raw.githubusercontent.com/MeRahulAhire/country-calling-code-html/master/phone_icon.png',
CountryCode: ''
};
handleSelectChange = (event) => {
const value = event.target.value;
const countryCode = event.target[event.target.selectedIndex].getAttribute('data-country-code');
const countryFlagImg = `https://flagpedia.net/data/flags/h80/${countryCode.toLowerCase()}.webp`;
this.setState({
select: value,
CountryFlag: countryFlagImg
});
}
render() {
const { CountryFlag, CountryCode, step } = this.state;
const values = { CountryFlag, CountryCode };
switch (step) {
case 1:
return (
<StepOne
handleChange={this.handleSelectChange}
countryFlagHandler={this.countryFlagHandler}
values={values}
/>
)
default:
return (<h1>hello React App</h1>)
};
}
}
And child Component StepOne
import React, { Component } from 'react'
class StepOne extends Component {
render() {
const { values, handleChange } = this.props;
return (
<div>
<div class="image">
<img src={values.CountryFlag} id="img" />
</div>
<select id="country" onChange={this.props.handleChange} defaultValue={values.select}>
<option data-country-code="IN" value="91">India</option>
<option data-country-code="US" value="1">US</option>
<option data-country-code="GB" value="44">UK</option>
</select>
</div>
)
}
}