I'm trying to insert data into an empty JSON array, but am having trouble. I'm defining the array in the constructor, then making a couple get requests to the back-end when the page loads, and after getting the response I want to add the new array element to the existing. This is the code I am using:
constructor() {
super();
this.state = {
sds: []
}
}
componentDidMount() {
axios.get('/userData', {
params: {
user: this.props.auth.user.name
}
}).then(res => {
for(var i=0; i<res.data[0].chemID.split(',').length; i++){
if(res.data[0].chemID.split(',')[i] != 0){
axios.get('/chemData', {
params: {
id: res.data[0].chemID.split(',')[i]
}
//This is where I want to insert the data
}).then(res => this.sds += ({
id: i,
title: res.data[0].chemName,
selected: false,
key: 'sds'
}))
}
}
})
}
+= doesn't work like that. Use a spread operator to copy the previous contents of the array, then add the new object in manually -
}).then((res) => {
const newThing = {
id: i,
title: res.data[0].chemName,
selected: false,
key: 'sds'
};
this.setState(prevState => ({
sds: [...prevState.sds, newThing]
}))
}
You should never try to mutate the state yourself, always use setState. In this case, you can pass a function as the first parameter, which provides the previous state. That way, you can ensure whatever was in this.state.sds is preserved, and your new object is added to that array.
You can use array.push().
this.state.sds.push(obj);
If you are not using react setState method than, you need to refer any state variable using this.state.variableName.
You need to add to your array by using the push() method like this:
constructor() {
super();
this.state = {
sds: []
}
}
componentDidMount() {
axios.get('/userData', {
params: {
user: this.props.auth.user.name
}
}).then(res => {
for(var i=0; i<res.data[0].chemID.split(',').length; i++){
if(res.data[0].chemID.split(',')[i] != 0){
axios.get('/chemData', {
params: {
id: res.data[0].chemID.split(',')[i]
}
//This is where I want to insert the data
}).then(res => {
this.state.sds.push({
id: i,
title: res.data[0].chemName,
selected: false,
key: 'sds'
})
})
}
}
})
}
You can try using the next example:
this.state.sds[this.state.sds.length] = {
id: i,
title: res.data[0].chemName,
selected: false,
key: 'sds'
}
[Edited]
Like #larz said, you must use the setState method to avoid unexpected result of your code.
var newSds = this.stage.sds;
newSds[newSds.length] = {
id: i,
title: res.data[0].chemName,
selected: false,
key: 'sds'
};
this.setState({ sds: newSds});
You can get more information about the lifecycle in react here and the "state updates are merged" here
Related
this is my code
const [state, setState] = useState(
[{id: 1, key:""}, {id: 2, key:""}, {id: 3, key:""}]
)
i want to to change "key" state
im confuse
now im using
setState(
[...state].map((data, index) => {
if (data.id === state[index].id) {
return {
...data,
key: result,
};
} else return data;
}),
);
}
result variable came from result when i fetching data.
result is a random string
If your data structure is always going to be in that order data.id === state[index].id doesn't really achieve much.
For example:
when data.id is 1 the index will be 0. And state[0].id is 1.
when data.id is 2 the index will be 2. And state[1].id is 2.
etc.
It just sounds like you want to iterate over all the objects in state and update each key value with that random string you mentioned in the comment section. There's no need to make a copy of state since map already returns a new array ready for setState to use.
function setState(mapped) {
console.log(mapped);
}
const state = [{ id: 1, key: '' }, { id: 2, key: '' }, { id: 3, key: '' }];
const result = 'random';
const mapped = state.map(data => {
return { ...data, key: result };
});
setState(mapped);
I have this type of state in my app
state = {
abc: true,
array: [
{ id: 12345, done: false },
{ id: 10203, done: false },
{ id: 54321, done: false }
]
};
I am looking for a solution to the following problem: I need to change done property accordingly to passed id like in the following function when something like this handle(12345) is passed as an argument to handle function :
handle = id => {
this.state.array.map(e => {
if (e.key === id) {
this.setState({array: [
{ id: id, done: true },
{ id: 10203, done: false },
{ id: 54321, done: false }
]})
}
});
};
In simple words I need to change just one object in array based on provided id.
Thanks for any help or tips!
I'd write the handle method as:
handle = id => {
this.setState(prevState => {
const { array } = prevState;
return {
array: [
...array.filter(o => o.id !== id),
{id, done: true}
]
};
});
};
The idea here is that, remove the matching id from old state, and then add a new object to the array with id and done property as {id, done: true}.
Once you are allowed to restructure state to be hashmap instead of array:
state = {
abc: true,
array: {
12345: { done: false },
10203: { done: false },
54321: { done: false }
]
};
then you will be able to use power of spread operator:
let id = 12345;
this.setState({
array: {
...this.state.array,
[id]: {
...this.state.array[id],
done: true
}
}
});
Otherwise using array.map() seems to be the only option
You can use this Redux pattern to return a new array with the element in question being changed, while keeping your array immutable:
handle = id => {
const updatedArray = this.state.array.map(e => {
if (e.key === id) {
return { id: id, done: true };
}
else {
return e;
}
});
this.setState({array: updatedArray});
};
This keeps your data structures immutable while changing only what you need to change.
var newArray = this.state.array;
for(var i = 0; i < newArray.length; i++){
if(newArray[i].id === 12345) {
newArray[i].done = true;
}
}
this.setState({array: newArray});
By creating the newArray here you will be avoiding directly touching the state element, so you can change anything you want inside it afterwards you can set the state.
I have nested state which is needed to set its state by key's value.
state = {
income_source: {
type: 'text', label_name: 'Income Source', value: '', helper: 'dropdown',
},
employment_status: {
type: 'text', label_name: 'Employment Status', value: '', helper: 'dropdown',
},
...
I'm getting huge list from get_financial_assessment object and the code becomes messy when I tried to setState in componentDidMount().
async componentDidMount() {
let { get_financial_assessment } = await DAO.getFinancialAssessment()
if( get_financial_assessment ) {
const {
account_turnover,
cfd_score,
education_level,
employment_industry,
employment_status,
estimated_worth,
financial_information_score,
income_source,
net_income,
occupation,
source_of_wealth,
total_score,
trading_score,
} = get_financial_assessment;
this.setState(prevState => ({
anticipated_account_turnover: {...prevState.anticipated_account_turnover, value: account_turnover},
occupation: {...prevState.occupation, value: cfd_score},
level_of_education: {...prevState.level_of_education, value: education_level},
source_of_wealth: {...prevState.source_of_wealth, value: employment_industry},
net_annual_income: {...prevState.net_annual_income, value: employment_status},
estimated_net_worth: {...prevState.estimated_net_worth, value: estimated_worth},
source_of_wealth: {...prevState.source_of_wealth, value: financial_information_score},
}));
} else {
console.log('nope');
}
}
UPDATE
1) One possible approach might be,,, once we get the data, we can make an object and setState the object to the state. -> It might be the best possible answer?
2) Any other approach???
You can use Object.entries() to iteratively map them to the new object with updated value.
It seems like you are keeping some static data that will not be updated via API in the state though, so a better approach might be to keep them in a separate variable:
const metaData = {
income_source: {
type: 'text', label_name: 'Income Source', helper: 'dropdown' // no `value` here
},
...
}
Then your state will just need to contain the actual dynamic data:
state = {
income_source: '',
employment_status: ''
...
}
And updating it will simply be:
this.setState(get_financial_assessment);
You can also use object destructuring to avoid setting unwanted states:
const { trading_score, unwantedProperty, ...imptData } = get_financial_assessment;
this.setState(imptData);
If you want to have a simpler initial state, you can also do this:
state = {
data: {}
}
....
this.setState({ data: get_financial_assessment })
...
// in `render`, provide default value if property is not set
{this.state.data.income_source || ''}
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.
Basically what I need is to get "items" from json and re-assign it, and leave other keys untouched:
The first approach duplicates data.
The second seems to be bad in terms of performance.
And the third is hard to read and understand.
I use lodash. But if it can't be done in a clever fashion, you can suggest me a different library.
function a(name, json) {
return {
type: RECEIVE_DATA,
name,
items: _.get(json, 'data.items', []),
receivedAt: Date.now(),
...json,
};
}
function b(name, json) {
return {
..._.omit(json, 'data'),
type: RECEIVE_DATA,
name,
items: _.get(json, 'data.items', []),
receivedAt: Date.now(),
}
}
function c(name, json) {
return {
..._.transform(json, (result, value, key) =>{
if (key === 'data') {
result['items'] = value['items'];
} else {
result[key] = value;
}
}, {}),
type: RECEIVE_DATA,
name,
receivedAt: Date.now(),
}
}
Use parameters destructuring to get items, and rest properties to collect the rest of the params:
const json = { another: [], data: { items: [1] } };
function a(name, { data, data: { items = [] }, ...jsonRest } = {}) {
return {
type: 'RECEIVE_DATA',
name,
items,
receivedAt: Date.now(),
...jsonRest
};
}
console.log(a('name', json));
You can also shorten the action creator a bit by using an arrow function:
const a = (name, { data, data: { items = [] }, ...jsonRest } = {}) => ({
type: RECEIVE_DATA,
name,
items,
receivedAt: Date.now(),
...jsonRest
});
If you don't mind having a couple more lines in your function, you could easily just grab the data from that key then delete it:
function a(name, json = {}) {
const { items = [] } = json.data;
delete json.data;
return {
type: RECEIVE_DATA,
name,
items,
receivedAt: Date.now(),
...json,
};
}