How to set object state in react - javascript

I have the following code:
this.state = {
user: null
}
If the user has a name property how would I set it knowing that this.setState({user.name: 'Bob') won't work.

this.setState({ user: {...this.state.user, name: 'Bob'} });

const copy = {...this.state.user}
copy.name = 'Bob'
this.setState({ user: copy })
and if you don't want to override existing properties like say age do this
const copy = {...this.state.user}
copy.name = 'Bob'
this.setState({ user: { ...user, name: copy } })

user itself is probably an object so you can set the state of user to an object
this.setState({ user: { name: "bob" }})
or
this.state = {
user: {
name: "bob"
}
}

If your state contains an object, then you can update the value of nested state using the javascript spread operator.
Other user parameters will not be affected.
this.setState({
...this.state,
user: {
...this.state.user,
name:'Bob'
}
})

You can use spread operator for this like:
this.setState(prevState => ({
user: { ...prevState.user, name: 'Bob' }
}))
For more info:
Using State Correctly

Related

the previous state is not saved - React useatate

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
}

Better way to empty all the values of an object by setState in react

I am using React. This is my state
state = {
customerData: {
id: '',
name: '',
type: '',
place: '',
country: '',
timezone: 'GMT+5:30',
status: false
}
}
There is an edit functionality where the customerData object gets populated on click of edit button. I am showing this data in a modal.
Now in the modal, when I click the submit button, the modal should hide and the data populated in the customerData object should be empty. I can do it like this:
this.setState({
customerData: {
...this.state.customerData
id: '',
name: '',
type: '',
place: '',
country: '',
timezone: '',
status: false
}
}
})
I am wondering is there any one line way in ES6 to make the customerData Object empty. Because, if there are too many fields, it will be difficult.
There are two easy options here:
Create a default object
Above your component you can create the 'empty' value for your state. For example:
const emptyCustomerData = {
id: '',
name: '',
type: '',
place: '',
country: '',
timezone: '',
status: false,
}
When you want to clear your state object, now you just call:
this.setState({
customerData: emptyCustomerData,
})
Allow all the values of customerData to be nullable
You could simply set customerData to an empty object:
this.setState({
customerData: {},
})
Doing this means that before using any of the properties in your code, you need to check for undefined:
// This sets myVal to customerData.name, or if name is undefined, to an empty string
const myVal = this.state.customerData.name || ''
You can set a variable like initialState:
const initialState = {
customerData: {
id: '',
name: '',
type: '',
place: '',
country: '',
timezone: 'GMT+5:30',
status: false
}
}
And before you hide the modal, do a:
this.setState(initialState)
Assuming you want to preserve the customerData keys, for your use case it may be sufficient to set everything to null which would simplify the reset:
this.setState(customerData: Object.assign(...Object.keys(this.state.customerData).map(k => ({[k]: null}))))
https://jsfiddle.net/0ymx7fsq/
Alternatively, since you're using class components you can also save the initial state in a constructor as well:
constructor(props) {
super(props)
this.state = initialState;
}
...
reset() {
this.setState({ customerData : initialState.customerData });
}
...
You can use reduce in order not to rely on params count and number. It should looks like this
const data = {
id: '1',
name: '2',
type: '3',
place: '4',
country: '5',
timezone: '6',
status: true
}
function getDefaultObject(obj) {
const defaultValues = {
'string': '',
'boolean': false
}
return Object.keys(obj).reduce((acc, rec, index) => {
return { ...acc, [rec]: defaultValues[typeof obj[rec]]}
}, {})
}
console.log(getDefaultObject(data))
Just facing your problem today and i solved it with this code
const handleResetState = () => {
const {customerData} = this.state
let emptyState = {};
for (const key in customerData){
emptyState = {...emptyState, [key] : ""}
}
//i think you will more need the second option, so dont use first option
//normal change (first option)
this.setState({customerData: emptyState});
//in case if your key need to have special initial value that are not empty string (second option)
this.setState({customerData: {...emptyState, status: false} })
}

Assign values from one array into another in React

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.

React-Redux: State updated but not copied

In my reducer I can't understund why my state returns updated but not copied.
const loginReducer = (state = {}, action) => {
let newstate = JSON.parse(JSON.stringify(state))
const { type, payload } = action
switch(type) {
case constant.LOGIN_SUCCESS:
//
newstate = { login: [{ user: payload.user, password: payload.password, logged: true }] }
console.log(newstate.login)
break;
default:
break;
}
return newstate
}
Surely it's a simple thing what I'm missing, but I can't figure out it
It looks as though you are completely re-assigning newstate in this line:
newstate = { login: [{ user: payload.user, password: payload.password, logged: true }] }
If you want to maintain the current values in state you need to also include them:
newstate = { ...newstate, login: [{ user: payload.user, password: payload.password, logged: true }] }
The above uses object spread, which you may or may not have in your babel configuration. You can also use Object.assign to achieve the same thing:
newstate = Object.assign(newstate, { login: [{ user: payload.user, password: payload.password, logged: true }] });

react redux merge state with given array

Lets say I have a reducer which is like :
const initialState = [
{
accessToken: null,
isLoggedIn: false,
}
]
export default function my_reducer(state = initialState, action) {
switch (action.type) {
case LOGIN:
return state.merge(user: action) ---> how to handle this
and the output should be like:
[
{
accessToken: null,
isLoggedIn: false,
user: {
name: 'some name',
email: 'some email'
}
]
In action I am getting a array which I am providing by doing JSON.stringify(response)
previous data should not be changed and new data should be updated
The ES6 Way
To create a new object with the state in ES6 we can use the spread operator. Like this
...
case ActionType.SUCCESS_GET_DATA : {
let newState = { ...state, [action.uniqueKey]: action.payload };
return state.merge(newState);
}
...
I did the uniqueKey part as a variable because you will want a unique name for your state.
IMO this syntax is much easier to understand than the Object.assign
You can use Object.assign() function:
var state = {
accessToken: null,
isLoggedIn: false,
};
var user = {
name: 'some name',
email: 'some email'
};
var newState = Object.assign({}, state, {user});
console.log(newState);
First I see that your state is actually an array, but I think you would need an object right?
So it would be:
const initialState = {
accessToken: null,
isLoggedIn: false,
}
(requires Babel) So with spread operator you can:
return {
...initialState,
user: {
name: '...',
surname: '...'
}
};
Or if you do not transpile via Babel alike:
return Object.assign({}, initialState, {
user: {
name: '...',
surname: '...'
}
});
Using ES6 spread syntax
...
case 'ACTION_TYPE_A': {
return { ...state, action.key: action.value };
}
...
This will return the merged state by updating the 'key' if it exists in the original state.
Everything according to new Es6 format : )
A total addToDo reducer function where the data is appended to the previous state. And you get the output of a new state data : )
export const addToDo = (state, action) => {
const { name, email, phone, image,key } = action;
var data = [...state.data];
var newData = {
name: name, email: email, phone: phone, image: image,key:key
}
data.push(newData)
return(
state.merge({
data : data
})
)};
Happy coding.

Categories