Yannick Panis Yannick Panis - 3 years ago 206
React JSX Question

unintentional state update in react form

First of all this is a simplified example: Codepen Project

I am building an edit form in react, which checks if there are any changes.
You can only save the form if there are any changes and any changes you made will be shown by changing the style (border-left) on the matching input element. It looks like this

enter image description here

To do that I am saving the original data/ state in the component state in the componentDidMount method and compare that to the state of the different inputs.

componentDidMount() {
// if the project is accessed from home and is not a new project, project data will be passed along
if (this.props.project) {
this.setState({
name: this.props.project.name,
tags: this.props.project.tags
}, this.setInitialState)
} else if (this.props.edit && this.props.match.params.id) {
// instead of an api call to get project data, if the project is accessed directly by url
const project = projects.find((project) => project.name === this.props.match.params.id)
this.setState({
name: project.name,
tags: project.tags
}, this.setInitialState)

}
// if there are no project data or an edit prop, it's a new project and the initialState remains empty
}


On each Input Change the input Value is compared to the initialState:

compareInputData() {
const formFields = {
name: {
ref : this.name,
changed : false
},
tags: {
ref : this.tagList,
changed : false
}
}

const state = this.state
const first = this.state.initialState

const nameHasChanged = state.name !== first.name
const tagsHaveChanged = state.tags.length !== first.tags.length

nameHasChanged
? ( formFields.name.changed = true )
: ( formFields.name.changed = false )

tagsHaveChanged
? ( formFields.tags.changed = true )
: ( formFields.tags.changed = false )

nameHasChanged || tagsHaveChanged
? (this.setState({
isChanged: true
}))
: (this.setState({
isChanged: false
}))

this.handleChangedInputStyles(formFields)
}


If there are changes the styling of the matching element is changed:

handleChangedInputStyles(formFields) {
const formFieldKeys = Object.keys(formFields)

formFieldKeys.map(key => {
formFields[key].changed
? formFields[key].ref.style.borderLeft = `2px solid orange`
: formFields[key].ref.style.borderLeft = '1px solid black'
})


}

That is working the way I want it to on normal input fields, but I am also saving related tags as an array, which are displayed as a list. Whenever I update that list (this.state.tags) my original state for the tags is being updated as well (this.state.initialState.tags), which means that I cannot pick up changes in my tag List.
However it does work if I am creating adding a tag to a new project instead of editing an existing one...
I have no idea how to fix that issue, since I don't really know what's causing it and I would love some help.

Thank you for reading through this post :)

Answer Source

Do not store this.state.initialState in the state. Store it in a member instead. For instance:

constructor(props) {
  this.initialState = Object.assign({}, whatever...);
  this.initialState.tags = [].concat(this.initialState.tags); // Keep a shallow copy of this array.
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download