Store object from API to state - javascript

I am trying to use ReactJS to store info provided by a backend API.
My backend API returns a JSON object which contains:
"a": 123
"b": 345
"c": 0
The above object is stored under data from my backend API.
So now I would like to store these values in data separately in React State as follows:
this.state = {
first:'',
second: '',
third: '',
}
//ComponentDidMount happen here
.then(result => {
first: //result.data.data value "a":123 will be stored to State *First*
//and "b":345 will go to second and "c":0 to third
}
debugger;
})
What should I write for that part for storing information to state?

If you are fine with using 0, 1, 2 instead 'first', 'second', 'third', you can map through the values of the object and for each index store it in state
.then(result => Object.values(result).map((value, i) => this.setState({ [i]: value })))
Do notice that the actual keys in the state gonna be in a type of string, so you can access them like this.state['0']

You may try
//ComponentDidMount happen here
.then(result => {
first.setState(result.data.data.a);
second.setState(result.data.data.b);
third.setState(result.data.data.c);
}
debugger;
})

Related

Why these HTML elements added to DOM using react/nextjs are not visible? [duplicate]

I seem to be having issues pushing data into a state array.
I am trying to achieve it this way:
this.setState({ myArray: this.state.myArray.push('new value') })
But I believe this is incorrect way and causes issues with mutability?
Using es6 it can be done like this:
this.setState({ myArray: [...this.state.myArray, 'new value'] }) //simple value
this.setState({ myArray: [...this.state.myArray, ...[1,2,3] ] }) //another array
Spread syntax
Array push returns length
this.state.myArray.push('new value') returns the length of the extended array, instead of the array itself.Array.prototype.push().
I guess you expect the returned value to be the array.
Immutability
It seems it's rather the behaviour of React:
NEVER mutate this.state directly, as calling setState() afterwards may
replace the mutation you made. Treat this.state as if it were
immutable.React.Component.
I guess, you would do it like this (not familiar with React):
var joined = this.state.myArray.concat('new value');
this.setState({ myArray: joined })
Functional Components & React Hooks
const [array,setArray] = useState([]);
Push value at the end:
setArray(oldArray => [...oldArray,newValue] );
Push value at the start:
setArray(oldArray => [newValue,...oldArray] );
Never recommended to mutate the state directly.
The recommended approach in later React versions is to use an updater function when modifying states to prevent race conditions:
Push string to end of the array
this.setState(prevState => ({
myArray: [...prevState.myArray, "new value"]
}))
Push string to beginning of the array
this.setState(prevState => ({
myArray: ["new value", ...prevState.myArray]
}))
Push object to end of the array
this.setState(prevState => ({
myArray: [...prevState.myArray, {"name": "object"}]
}))
Push object to beginning of the array
this.setState(prevState => ({
myArray: [ {"name": "object"}, ...prevState.myArray]
}))
You should not be operating the state at all. At least, not directly. If you want to update your array, you'll want to do something like this.
var newStateArray = this.state.myArray.slice();
newStateArray.push('new value');
this.setState(myArray: newStateArray);
Working on the state object directly is not desirable. You can also take a look at React's immutability helpers.
https://facebook.github.io/react/docs/update.html
Here you can not push the object to a state array like this. You can push like your way in normal array.
Here you have to set the state,
this.setState({
myArray: [...this.state.myArray, 'new value']
})
You can use .concat method to create copy of your array with new data:
this.setState({ myArray: this.state.myArray.concat('new value') })
But beware of special behaviour of .concat method when passing arrays - [1, 2].concat(['foo', 3], 'bar') will result in [1, 2, 'foo', 3, 'bar'].
Using react hooks, you can do following way
const [countryList, setCountries] = useState([]);
setCountries((countryList) => [
...countryList,
"India",
]);
This Code work for me :
fetch('http://localhost:8080')
.then(response => response.json())
.then(json => {
this.setState({mystate: this.state.mystate.push.apply(this.state.mystate, json)})
})
React-Native
if u also want ur UI (ie. ur flatList) to be up to date, use PrevState:
in the example below if user clicks on the button , it is going to add a new object to the list( both in the model and UI)
data: ['shopping','reading'] // declared in constructor
onPress={() => {this.setState((prevState, props) => {
return {data: [new obj].concat(prevState.data) };
})}}.
In the following way we can check and update the objects
this.setState(prevState => ({
Chart: this.state.Chart.length !== 0 ? [...prevState.Chart,data[data.length - 1]] : data
}));
setState([...prevState, {
label: newState.name,
value: newState.id
}]);
Was working with the dropdowns and wanted to implement this scenario there, i found this simple solution for dropdown with multiple values.
you are breaking React principles, you should clone the old state then merge it with the new data, you shouldn't manipulate your state directly,
your code should go like this
fetch('http://localhost:8080').then(response => response.json()).then(json ={this.setState({mystate[...this.state.mystate, json]}) })
If you use:
const[myArr, setMyArr] = useState([]);
for add:
setMyArr([...myArr, value]);
and for remove:
let index = myArr.indexOf(value);
if(index !== -1)
setPatch([...myArr.slice(0, index), ...myArr.slice(index, myArr.length-1)]);
I guess this is a little bit late for an answer but for those new to react
You can use this tiny package called immer
see this example: https://immerjs.github.io/immer/produce

fetch gives me a full array but it is empty when i try to access it

I get an object from a Symfony rest API. This object has a property "shooting" which is an array and this array is full when I console.log it but when i try to access it, it is empty
This is my fetch request
const getProjectsAvailable = async () => {
const data = await fetch(
`${process.env.GATSBY_CORE_URI}/api/dashboard/supplier/projects/available`,
{
headers: {
[`X-Auth-Token`]: `${token}`,
[`Accept`]: `application/json`,
},
}
);
return data;
};
Here is the project object that i get back from fetch request
0: {id: 258, name: "Project26-1", reference: "A6568", isOfferValidated: null, source: "dashboard", …}
It has a shooting key which contains an array and it is not empty
shootings: Array(1)
0:
addressCity: "Paris"
addressCountry: {id: 76}
But when i set this object to my component state, all values stay the same except the shooting key which becomes an empty array
const [projects, setProjects] = useState([]);
useEffect(() => {
getProjectsAvailable().then(res =>
res.json().then(data => {
setProjects(data);
})
);
}, []);
I have no idea why does it act like that.
Thanks in advance
EDIT :
For example, the first line with console.log gives me the response object with a full shooting array while the second one sets it to my state but shooting array is empty
useEffect(() => {
getProjectsAvailable().then(response => console.log(response));
getProjectsAvailable().then(response => setProjects(response));
}, []);
Ok it is my bad. Somewhere else in the code, there was a .split() on the shooting property which mutates the array so the props changed and shooting array got empty

useState won't update state

I'm trying to update a state using useState hook, however the state won't update.
const handleSelect = address => {
geocodeByAddress(address)
.then(address => {
const receivedAddress = address[0];
const newAddress = {
street: receivedAddress.address_components[1].long_name,
number: receivedAddress.address_components[0].long_name,
city: receivedAddress.address_components[3].long_name,
country: receivedAddress.address_components[5].long_name,
zip: receivedAddress.address_components[6].long_name,
selected: true
};
handleAddressSelection(newAddress);
})
.catch(error => console.log(error));
};
When handleSelect is called, it creates the object newAddress, and then calls handleAddressSelection passing newAddress.
function handleAddressSelection(newObj) {
console.log(newObj);
Object.keys(newObj).forEach(function(key) {
setValues({ ...values, [key]: newObj[key] });
});
}
In console.log(newObj) the object is filled fine, with all the data I need. Then I call setValues for each object in newObj, however no matter what, the values object won't receive the new data. The only one that is updated is selected: true, all others won't update.
What should I do to fix it?
You're calling setValues multiple times in a loop, and every time you do so, you spread the original values, and thus overwrite anything that was done on the previous setValues. Only the very last setValues ends up working, which happens to be the one for selected: true
If you need to base your update on the previous value of the state, you should use the function version of setValues, as in:
Object.keys(newObj).forEach(function(key) {
setValues(oldValues => ({ ...oldValues, [key]: newObj[key] }));
});
But even better would be to only call setValues once. If you're calling it multiple times, then you're going to generate multiple renders. I'd do this:
setValues(oldValues => ({...oldValues, ...newObj}));
Values is not even defined anywhere in your examples. My guess is, it's some cached copy and you should be using callback variant of the state setter instead:
setValues(previousValues => ({ ...previousValues, [key]: newObj[key] }));

Filtering away javascript objects into an array when using fetch

I have a react application, where I use the axios library, to get some values, and set them into an array of javascript objects in my state
componentDidMount(){
axios.get('http://localhost:8080/zoo/api/animals')
.then(res => this.setState({animals: res.data}))
}
Now I want to check if the objects, contains an Owner object, inside it, and filter out does that does,
First, I tried making a const, and then using the filter, to check if they contain the objects, and then set the state, but I can't save my values in a local variable
componentDidMount(){
const animals= [];
axios.get('http://localhost:8080/zoo/api/animals')
.then(res => animals=res.data)
console.log(animals) // logs empty array
console.log('mounted')
}
how can I make it so, that I can only get the animals that do NOT, have an owner object inside it?
Your animal array is empty in your second example because axios.get is asynchronous, what is in your then will be executed once the data is fetch, but the function will keep on going in the meantime.
To filter out your array, simply use filter right after fetching your data :
componentDidMount(){
axios.get('http://localhost:8080/zoo/api/animals')
.then(res => this.setState({animals: res.data.filter(animal => !animal.owner)}))
}
This function will filter out every animal object that does not have an owner property.
Working example :
const animals = [
{
name: 'Simba',
owner: {
some: 'stuff'
}
},
{
name: 1
}, ,
{
name: 2
}, ,
{
name: 3,
owner: {
some: 'stuff'
}
},
{
name: 'Bambi'
//status: 'dead'
}
]
console.log(animals.filter(animal => animal.owner))
EDIT: the answer was changed so that it only filters animals, that does not have an owner

Correct way to push into state array

I seem to be having issues pushing data into a state array.
I am trying to achieve it this way:
this.setState({ myArray: this.state.myArray.push('new value') })
But I believe this is incorrect way and causes issues with mutability?
Using es6 it can be done like this:
this.setState({ myArray: [...this.state.myArray, 'new value'] }) //simple value
this.setState({ myArray: [...this.state.myArray, ...[1,2,3] ] }) //another array
Spread syntax
Array push returns length
this.state.myArray.push('new value') returns the length of the extended array, instead of the array itself.Array.prototype.push().
I guess you expect the returned value to be the array.
Immutability
It seems it's rather the behaviour of React:
NEVER mutate this.state directly, as calling setState() afterwards may
replace the mutation you made. Treat this.state as if it were
immutable.React.Component.
I guess, you would do it like this (not familiar with React):
var joined = this.state.myArray.concat('new value');
this.setState({ myArray: joined })
Functional Components & React Hooks
const [array,setArray] = useState([]);
Push value at the end:
setArray(oldArray => [...oldArray,newValue] );
Push value at the start:
setArray(oldArray => [newValue,...oldArray] );
Never recommended to mutate the state directly.
The recommended approach in later React versions is to use an updater function when modifying states to prevent race conditions:
Push string to end of the array
this.setState(prevState => ({
myArray: [...prevState.myArray, "new value"]
}))
Push string to beginning of the array
this.setState(prevState => ({
myArray: ["new value", ...prevState.myArray]
}))
Push object to end of the array
this.setState(prevState => ({
myArray: [...prevState.myArray, {"name": "object"}]
}))
Push object to beginning of the array
this.setState(prevState => ({
myArray: [ {"name": "object"}, ...prevState.myArray]
}))
You should not be operating the state at all. At least, not directly. If you want to update your array, you'll want to do something like this.
var newStateArray = this.state.myArray.slice();
newStateArray.push('new value');
this.setState(myArray: newStateArray);
Working on the state object directly is not desirable. You can also take a look at React's immutability helpers.
https://facebook.github.io/react/docs/update.html
Here you can not push the object to a state array like this. You can push like your way in normal array.
Here you have to set the state,
this.setState({
myArray: [...this.state.myArray, 'new value']
})
You can use .concat method to create copy of your array with new data:
this.setState({ myArray: this.state.myArray.concat('new value') })
But beware of special behaviour of .concat method when passing arrays - [1, 2].concat(['foo', 3], 'bar') will result in [1, 2, 'foo', 3, 'bar'].
Using react hooks, you can do following way
const [countryList, setCountries] = useState([]);
setCountries((countryList) => [
...countryList,
"India",
]);
This Code work for me :
fetch('http://localhost:8080')
.then(response => response.json())
.then(json => {
this.setState({mystate: this.state.mystate.push.apply(this.state.mystate, json)})
})
React-Native
if u also want ur UI (ie. ur flatList) to be up to date, use PrevState:
in the example below if user clicks on the button , it is going to add a new object to the list( both in the model and UI)
data: ['shopping','reading'] // declared in constructor
onPress={() => {this.setState((prevState, props) => {
return {data: [new obj].concat(prevState.data) };
})}}.
In the following way we can check and update the objects
this.setState(prevState => ({
Chart: this.state.Chart.length !== 0 ? [...prevState.Chart,data[data.length - 1]] : data
}));
setState([...prevState, {
label: newState.name,
value: newState.id
}]);
Was working with the dropdowns and wanted to implement this scenario there, i found this simple solution for dropdown with multiple values.
you are breaking React principles, you should clone the old state then merge it with the new data, you shouldn't manipulate your state directly,
your code should go like this
fetch('http://localhost:8080').then(response => response.json()).then(json ={this.setState({mystate[...this.state.mystate, json]}) })
If you use:
const[myArr, setMyArr] = useState([]);
for add:
setMyArr([...myArr, value]);
and for remove:
let index = myArr.indexOf(value);
if(index !== -1)
setPatch([...myArr.slice(0, index), ...myArr.slice(index, myArr.length-1)]);
I guess this is a little bit late for an answer but for those new to react
You can use this tiny package called immer
see this example: https://immerjs.github.io/immer/produce

Categories

Resources