I have a state object that looks like this.
this.state = {
formValues: {},
};
After some processing, formValues contains the following.
this.state = {
formValues: {
q1: value 1,
q2: value 2
},
};
Now i have q3 inside formValues which is an array of values. When i try to push the value like as follows
let q3 = e.target.name,
arrayValues = [1,2,3]
formValues[q3].push(arrayValues)
I am getting the following error while submitting the data
Uncaught Error: A state mutation was detected between dispatches
It looks like there is a problem with pushing data into array. Any idea on how to fix this?
You need to create a copy and update the state with setState instead of direct state mutation with push.
this.setState(prevState => ({
formValues: {
...prevState.formValues,
[q3]: prevState.formValues[q3].concat(arrayValues),
},
}));
assuming that you always want to push to your array such as in your use case:
const { formValues } = this.state
const arrayValues = [1,2,4]
const newFormValues = { ...formValues, q3: [...formValues[q3], arrayValues]}
this.setState({ formValues: newFormValues })
but much better if you control directly the value of q3:
const { formValues } = this.state
const arrayValues = [1,2,4]
const newFormValues = { ...formValues, q3: arrayValues}
this.setState({ formValues: newFormValues })
Related
I'm trying to save objects to an array, but I can't do it, the old state is deleted. I have two states in my component, from two different forms, the first form is just text and I get the data by "handleChange", but the second form is several objects that I want to store in an array that I get by "handleChangeArray".
const [formCompra, setFormCompra] = useState({
name: '',
lastName: '',
items: []
});
const [restForm, setRestForm] = useState();
const handleChage = (e) => {
const { name, value } = e.target;
setFormCompra({
...formCompra,
[name]: value
})
}
const handleChangeArray = (e) => {
const { name, value } = e.target;
setRestForm({
...restForm,
[name]: value
})
}
const handleSubmit = () => {
let newData = {
name: formCompra.name,
lastName: formCompra.lastName,
items: [...formCompra.items, restForm] //probably the error is here
}
console.log(newData)
}
As I mention, it is not possible to save the data in the array, I appreciate any help.
You can use the current state to set a new value, keeping all other values:
setState((current) => ({
...current,
key: newValue
}));
I think the issue may be that spread syntax only shallow copies the array, so in
const handleSubmit = () => {
let newData = {
name: formCompra.name,
lastName: formCompra.lastName,
items: [...formCompra.items, restForm] //probably the error is here
}
items is a copy of an array that points to all the original objects.
try
let newData = {
name: formCompra.name,
lastName: formCompra.lastName,
items: [...formCompra.map(x=>{...x}), {...restForm}] //probably the error is here
}
I need to declare a variable for formstate.title from this:
const initialState = { description: '', place: "", date: "", membersJoinedCounter: 0, title: "" };
const [formState, setFormState] = useState(initialState);
function setInput(key, value,) {
setFormState({ ...formState, [key]: value })
}
const userTitle = await API.graphql(graphqlOperation(getUserTitle, {id: userInfo.attributes.sub}))
formState.title = userTitle.data.getUser.title;
How to achieve that?
it's not clear what you wanted to ask, but assuming you want to set title value after fetching data, you can achieve that via using setFormstate method provided by useState hook (you can't directly set values as formstate returned by useState hook is immutable)
replace below line in your code
formState.title = userTitle.data.getUser.title
with below one
setFormState(prev=>({...prev,title:userTitle.data.getUser.title}))
I have this code in my constructor:
this.state = {
tests: [
{
question: "1",
answer: "2",
user: ""
},
{
question: "1",
answer: "2",
user: ""
},
],
};
I have edit function where I read event value in my input:
edit(id, event) {
this.state.tests[id].user = event.target.value;
this.setState({tests:this.state.tests});
}
But es give me this warning:
Do not mutate state directly. Use setState()
react/no-direct-mutation-state
What can i do in this case? Maybe somehow change the line with the assignment event.target.value into setState()?
You can use map() to create copy of tests
edit(id, event) {
const user = event.target.value;
const tests = this.state.tests.map((x,i) => i === id ? {...x, user} : x);
this.setState({tests});
}
One way I tend to go is to make a copy of the array first and then change an item in it, or change the array itself, and then set the state
var tests = this.state.tests.slice(0);
tests[id].user = event.target.value;
this.setState({tests:tests});
You may want to deep-clone the array in some cases, sometimes not.
You are correct, that the problem is with the line:
this.state.tests[id].user = event.target.value;
That's the point where you are mutating your state directly.
You have a few options.
You can "clone" the array first and then update it:
const newTests = [...this.state.tests];
newTests[id].user = event.target.value;
this.setState({tests: newTests});
You could also use immutability-helper:
const {value} = event.target;
this.setState(prevState => update(prevState, {[id]: {user: {$set: value}}}));
In this example, you need to extract value from your event, because it's not safe to access event values in asynchronous calls after an event has been handled.
edit(id, event) {
var newNote = {...this.state.tests[id]}
newNote.user = event.target.value
this.setState({ tests: [...this.state.tests, newNote]})
}
First of all, when you try to set a the new state using the data from previous state you have to use the updater as a function
https://reactjs.org/docs/react-component.html#setstate
const edit = (id, event) => {
this.setState((prevState) => {
const tests = [...prevState.tests];
tests[id] = {
...tests[id],
user: event.target.value
};
return {
...prevState,
tests
};
});
};
When state is not heavy, I use the following codes:
edit (id, event) {
const cp = JSON.parse(JSON.stringify(this.state.tests))
cp[id].user = event.target.value
this.setState({ tests: cp })
}
Update: I found Immer solves it perfectly.
import produce from 'immer'
edit (id, event) {
this.setState(
produce(draft => draft.tests[id].user = event.target.value)
)
}
I need to add an id number to the nested array in data called action. The code I'm using is:
const { data } = this.state
const newData = Object.assign([...data.action], Object.assign([...data.action],{0:'id' }))
but this code is not working. The result I am looking for is:
{id:1 action: "user...}
You can just use the spread operator.
const newData = {
...data,
action: {
...data.action,
id: 1
}
};
If action is an array, you can try something like this:
const newAction = data.action.map((actionItem, index) => ({
...actionItem,
id: index + 1
}));
const newData = {
...data,
action: newAction
};
I have the following structure of the state:
this.state = {
data: {
tags: []
},
items: [],
input: ''
};
When I submit the data, I am trying to assign the tags array with the items array data.
var newData = this.state.items.slice(); //copy array
this.setState({
...this.state,
data: { ...this.state.data, tags: newData }
});
The newData has all variables inside, but tags is always empty.
How can I assign exactly the same values into the tags array?
Here is my console log:
console.log(this.state.data.tags, this.state.items);
enter image description here
UPDATE:
onSubmit = (e) => {
e.preventDefault();
const errors = this.validate(this.state.data);
this.setState({ errors });
if (Object.keys(errors).length === 0) {
this.setState({ loading: true });
this.setState(prevState => ({
data: {
...prevState.data,
tags: prevState.items
}
}));
console.log(this.state.data.tags, this.state.items);
this.props
.submit(this.state.data)
.catch(err =>
this.setState({ errors: err.response.data.errors, loading: false })
);
}
};
When you set state, you don't spread the current state into the object you're passing. setState takes in an object as it's first argument and only updates the keys in that object. So, for example if we had:
this.state = {
a: 1,
b: 2
};
Using this:
this.setState({
b: 3
});
Will only update b. a will not be affected. Also, note that setState is asynchronous - and it's not guaranteed safe to reference state inside setState. Thus, React provides you with a callback to access the previous state:
this.setState(prevState => ({
data: {
...prevState.data,
tags: prevState.items
}
}));
This will only update the data object in state, and only the tag property, with the previous state's items array.