I am new to React, and I am designing a submission form with multiple fields in React. It has some Textfield input boxes and a Tags input feature. The user enters their info in fields and at the end, the info has to be shown on a confirmation page. Now all of my info are shown except for the Tags (from the tags input). What seems to be the problem?
The Form (AdditionalInfo.js)
export const TagsInput = props => {
const [Tags, setTags] = React.useState(props.Tags)
const removeTags = indexToRemove => {
setTags([...Tags.filter((_, index) => index !== indexToRemove)])
}
const addTags = event => {
if (event.target.value !== '') {
setTags([...Tags, event.target.value])
props.selectedTags([...Tags, event.target.value])
event.target.value = ''
}
}
return (
<div className='tags-input'>
<ul id='tags'>
{Tags.map((Tag, index) => (
<li key={index} className='tag'>
<span className='tag-title'>{Tag}</span>
<span
className='tag-close-icon'
onClick={() => removeTags(index)}
>
x
</span>
</li>
))}
</ul>
<input
type='text'
onKeyUp={event => event.key === 'Enter' ? addTags(event) : null}
placeholder='Press enter to add tags'
/>
</div>
)
}
export class AdditionalInfo extends Component {
continue = e => {
e.preventDefault();
this.props.nextStep();
};
back = e => {
e.preventDefault();
this.props.prevStep();
};
render() {
const {values, handleChange} = this.props
const selectedTags = Tags => {
console.log(Tags)
}
return (
<Container className='ContainerA'>
<Row>
<Col className='ACol'>
<br />
<br />
<div>
<TagsInput selectedTags={selectedTags} Tags={['Nodejs', 'MongoDB']} onChange={handleChange('Tags')}
defaultValue={values.Tags} />
</div>
<Row>
<Col xs='6' sm='4'>
<TextField
placeholder="Country"
label="Country"
onChange={handleChange('Country')}
defaultValue={values.Country}
margin="normal"
fullWidth="true"
id="outlined-basic"
variant="outlined"
required
/>
....
<div className='buttons-container' style={{position:'relative',bottom:'20px'}}>
<button onClick={this.back} className='previous'>قبلی</button>
<button form='my-form' type='submit' onClick={this.continue} className='next'>ادامه</button>
</div>
The Main Form Holder
//AdditionalInfo.js is used here
class CreateJob extends Component {
state = {
step:1,
Title:'',
requirements:'',
Country:'',
Region:'',
Zipcode:'',
Benefits:'',
Company:'',
InternalCode:'',
Details:'',
Tags:[],
Address:'',
Department:'',
Salary:''
}
nextStep =() => {
const {step} = this.state
this.setState({
step: step + 1
})
}
prevStep =() => {
const {step} = this.state
this.setState({
step: step - 1
})
}
handleChange = input => e => {
this.setState({ [input]: e.target.value });
};
render () {
const { step } = this.state
const {Title,Benefits,Company,InternalCode,Detailss,Tags,Address,Department,Salary,requirements,Country,Region,Zipcode } = this.state;
const values ={Title,Benefits,Company,InternalCode,Detailss,Tags,Address,Department,Salary,requirements,Country,Region,Zipcode}
return (
<div>
........
{(()=>{
switch (step) {
case 1:
return(
<AdditionalInfo
nextStep={this.nextStep}
handleChange={this.handleChange}
values={values}
/>
)
.........
And this is the confirmation Page
//it uses the main form holder component info too
export class Confirmation extends Component {
continue = e => {
e.preventDefault();
this.props.nextStep();
};
back = e => {
e.preventDefault();
this.props.prevStep();
};
render () {
const {
values: {
Title, Benefits,
Company, InternalCode, Detailss, Department,Tags, Salary,requirements,Zipcode,
Country,Region
}
} = this.props
return (
....
<div className='content'>
<Container>
<Row>
<Col xs='6' sm='4' className='TextContent'>
<Row className='TextInside'> {Salary} : حقوق پیشنهادی</Row>
<Row> زیپ کد : {Zipcode}</Row>
<Row> کشور : {Country} </Row>
..
</Col>
<Col xs='6' sm='4' className='TextContent'>
...
<Row> تگ ها : {Tags}</Row>
Sorry for the long code but I don't understand why the info of {Country} (for example) is shown in this Confirmation page but my tags from the TagsInput {Tags} aren't displayed (even though they are logged correctly into the Console) Where is my mistake?
Your AdditionalInfo
component ties each of its inputs to the handleChange
props, which was passed to it by the parent. When a field is updated, the parent learns about it.
Your TagsInput
component stores the list of tags in its own state (useState hook). It never informs the parent about any changes it has.
Later, when your parent (presumably, not shown) calls the ConfirmationPage
, it doesn't know about the tags that were entered, but it does know about the other info because it was told about them.
Instead, track the list of tags in the parent, and pass down the current list of tags (as well as an 'addTag' and 'removeTag' handler) to the child.
UPDATE
I have edited your sandbox to show a working example. Note I did have to remove a lot of references to outside files to get it to compile, so in the future you may want to strip down your code to the exact question you have before posting the sandbox.
https://codesandbox.io/s/tagger-u8fs5
Some notes:
updateParent
function that is called whenever you add or remove a tag.handleChange
function couldn't be used for the tags, because you have it set up to look for e.target.value
and in this case we are sending it an array of values directly, not an event with a value. I added a handleChangeRaw
function that allows this.setTags
) and then have the updateParent
function reference that state directly, because it will be one render behind (i.e. one tag behind).join(',')
to your tags output; otherwise it just tries to make a string out of the elements, which will run the words together.So in summary, the missing step was indeed that your parent component was never made aware of the changes in the tags. When you have nested components (parent/child relationship) you need to carefully pass the information up and down; they don't automatically share what they know.