Unable to update array state on delete [React.js] - javascript

I am working on a React.js app where I have a functional component which contains state array, which I declared like this const [myArray, setMyArray] = useState([]).
I fetch some data from an API and add it to the array with no problem. But I am unable to delete an element of the array. First I delete it through the API and then I try to update the array, but it seems to not work.
Here is the code sample:
const deleteElement = (id) => {
fetch(URL,{method: "DELETE"}).then((response) => response.json()).then(deleteFromArray(id))};
const deleteFromArray = (id) => {
const filtered = myArray.filter((item) => item.id !== id);
setMyArray(filtered)
}
The filtered array is ok, it does not contain the deleted element. But I just can't figure out why the myArray does not get updated here.
I am new to React so any hints would be appreciated. Thanks

The setMyArray runs asynchronously so if you wrote like below, the result is always the old value but the array updated successfully.
const deleteFromArray = (id) => {
const filtered = myArray.filter((item) => item.id !== id);
setMyArray(filtered)
console.log(myArray); // old value
}
To see the updated array, you should use useEffect like below:
useEffect(() => { console.log(myArray) }, [myArray]);
This hook will runs everytime the myArray changed.

Related

how can i get a object from json in react?

i have to get some data from a complex objects json (in my case is: valor_custo_fornecedor from the detalhe object, how can i do this ?
i already tried with axios like this:
const [valorCusto, setValorCusto] = useState([]);
useEffect(() => {
axios.get('MY API LINK').then((res) => {
let valor = res.detalhe.valor_custo_fornecedor;
setValorCusto(res.valorCusto.valor_custo_fornecedor);
});
});
It is hard to tell without seeing the complete json what key/value pairs exist in the data, but from what I can see you are passing a value that does not seem to exist in the data to setValorCusto, namely: res.valorCusto.valor_custo_fornecedor
I am guessing what you want to pass to setValorCusto is the valor variable that you have defined above? However the res.data.detalheis as others have replied an array, which you would either have to iterate through or specify an index for.
Another detail, not relating to the question in itself, is that I would add a dependency array to the effect, so that this api request is not called on every component re-render. So the end result would look something like this:
const [valorCusto, setValorCusto] = useState([]);
useEffect(() => {
axios.get('MY API LINK').then((res) => {
const valor = res.data.detalhe.map((detalheItem) => {
return detalheItem.valor_custo_fornecedor;
});
setValorCusto(valor);
});
}, []);
Notice the empty array as the second parameter in the useEffect call. This means that the effect will only be run on the initial rendering of the component.
Then you can access the value by simply using valorCusto wherever you need it in the component.
After setting response in setValorCusto ; you can loop over you state variable and get the desired value
Like the other answers state. What you are returning from your API is an array of objects. Currently in your code, that array lives inside the response object. So in this snippet below:
axios.get('MY API LINK').then((res) => {
let valor = res.detalhe.valor_custo_fornecedor;
You need to specify from which item in the array you would like to get the detalhe object from (like res[0].detalhe).
One way you could do if you like to use everything from the array, is to store the entire array in your useState. Afterwards, you could loop over the array and do something with the objects inside of it.
PS. If you're not entirely sure what the state holds. You could log it to the page by doing something like <div>{JSON.stringify(valorCusto)}</div>
export default function App() {
const [valorCusto, setValorCusto] = useState(null);
useEffect(() => {
const getData = async () => {
const res = await axios.get("API");
const data = await res.data;
setValorCusto(data);
};
getData();
}, []);
return (
<main>
<div>
{valorCusto.map((item) => {
<div>{item.id}</div>;
})}
</div>
</main>
);
}
You can convert json like this:
useEffect(() => {
axios.get('MY API LINK').then((res) => {
let obj = JSON.parse(res.detalhe.valor_custo_fornecedor)
});
});

Updating a state variable with a new reference doesn't trigger re-rendering in React

I'm writing a table component for my page in React.
I have a function loadData() that makes a request to an api. Using the api result to update the data state variable, using a new reference.
The problem here is that React doesn't trigger any re-render for the data variable.
const [data, setData] = useState([]);
const loadData = async () => {
try {
...
let response_json = await response.json();
setData(transformData(response_json.items));
...
}
const transformData = (data) => {
if (data === undefined || data === null) {
return [];
}
let new_data = [];
data.forEach((entry,index) => {
new_data.push(cloneElement(props.config.table_entry,{data:entry, key:index}, null));
});
return new_data;
}
I am using this code to change the table's page, making a request with parameters like pageNumber, pageSize, filters. So even with different data and different reference, still it doesn't trigger re-rendering.
This problem has challenged me for like one whole morning was that the data variable continued to updated on every request made but the webpage never re-rendered.
The answer lies here
data.forEach((entry,index) => {
new_data.push(cloneElement(props.config.table_entry,{data:entry, key:index}, null));
});
in the transformData function where it creates a new array of new data, BUT the key property of the component never changed because it was the index of its position in the array returned from the server.
Assigning the key to a unique id solved the problem.

Filter Array of objects (API response) based on flag in database

I am receiving a large array of objects using the below method:
const GetList = async () => {
const [data, error] = await GetList();
console.log(response);
This gives me back an array of 1000 elements, there is an isActive flag on every element and I need to filter out the elements that are not active.
I am not able to use
var resultFiltered = response.filter(obj =>{
return obj.isActive === 'true'
});
As it gives me an error message
The entire array is stored in the data variable, how can I filter out all elements that are not active?
I'm not sure why you are using response variable, since the value is fetched variable data, try filtering as shown below
const GetList = async () => {
const data = await GetList();
const resultFiltered = data.filter(obj => Boolean(obj.isActive) === true);
console.log(resultFiltered);
}
According to the error, the response variable is not an array-type variable please check that out. If it's an object then convert it into an array and then apply the filter function.
const resultFiltered = Object.values(response).filter(obj => obj.isActive === true);
Test the bellow code:
response.data.filter()

react js How to keep the initial data saved after applying the fitler on array

I am working on a React JS based UI and facing an issue wiht data .
I fetch data from an API and store it in an initial value by using useState hook.
Then I use useEffect to store it in 'lists' via setLists()
const [lists, setLists] = useState([]);
useEffect(()=>{
const url = "https://domain/api/all";
fetch(url).then(response => response.json()).then(response=> {
setLists(response.json.list);
});
Now I am able to use map to enter data in a table
Lists is an array object that has multiple values in it and I want t filter the table based on age and country
let [ageList, setAgeList] = useState([]);
let [countryList, setcoutryList] = useState([]);
useEffect(()=>{
var newList = lists.filter(e=> e.age === age);
setList(newList)
},[age])
useEffect(()=>{
setlist(lists.filter(e=> e.country === country))
},[country])
With this method first attempt of filter works fine by both Age and Country but when I change the value of the filter for age or country then I get empty array which is expected as the new lists variable has entered with last filtered country/age
How can I get over this?
As you noticed, when you overwrite your previous state with the filtered state, you lose the previous state forever. The solution here is to keep the initial value of lists and create a new variable for your filtered lists. Something like this:
const [lists, setLists] = useState([]);
const [ageFilter, setAgeFilter] = useState("");
const [countryFilter, setcountryFilter] = useState("");
const url = "https://domain/api/all";
fetch(url)
.then(response => response.json())
.then(response=> {
setLists(response.json.list);
});
let filteredByCountryAndAge = listfilter((listItem) => {
return (listItem.country === countryFilter && listItem.age === ageFilter);
});
(or however else you are filtering it).
The point being, do not change the value of lists after it is set with the entire list. When filtering, assign the criteria by which you are filtering to state variables, but assign the filtered data to a variable—NOT a state variable.
Do you have control over the fetched data? If so, the obvious solution would be to change the datamodel server side.
If not, I would suggest storing the initial response in a mutable object from useRef.
Edit
const [lists, setLists] = useState([]);
const cache = useRef([]);
useEffect(()=>{
const url = "https://domain/api/all";
fetch(url).then(response => response.json()).then(response=> {
cache.current = response.json.list;
setLists(response.json.list);
});
});

Too many re-renders when accessing data from an API in React

I am making a get request to an API that is linked to my database.
dataApi is a really big object with a lot of objects and arrays nested within it.
Some of the entries from the database are not having the full details that I need so I am filtering them to only show those with a length of > 5.
Now the issue is when I try to get the name of each entry which is split into either Tag1, Tag2 or Tag3.
Before this when I was accessing all the entries and getting the items within them there was no issue.
But when I try to filter them by the name and store the objects corresponding to that name in its state this issue arrises.
Edit:
When I console.log(arr1) it shows all the data but the moment I set the state to it it causes the error.
// Data from all entries in database
const [dataApi, setDataApi] = useState();
// Data for each of the tags
const [tag1, setTag1] = useState();
const [tag2, setTag2] = useState();
const [tag3, setTag3] = useState();
useEffect(() => {
axios.get(URL).then((res) => {
const data = res.data;
setDataApi(data);
});
}, []);
const getTagDetails = data => {
const arr1 = [];
const arr2 = [];
const arr3 = [];
data &&
data.forEach(d => {
// Entries into the database which do not have any tag information
// have a size of 5 and those with all the details have a size of 6
const sizeOfObject = Object.keys(d).length;
// Only need database items with all the details
if (sizeOfObject > 5) {
const name = d["tags"]["L"][0]["M"]["name"]["S"];
// Split the data for the tags into their respective name
// Will be used to set individual datasets for each tag
if (name === "Tag1") {
arr1.push(d);
}
if (name === "Tag2") {
arr2.push(d);
}
if (name === "Tag3") {
arr3.push(d);
}
}
});
setTag1(arr1);
setTag2(arr2);
setTag3(arr3);
};
getTagDetails(dataApi);
I guess the problem is you call getTagDetails(dataApi) inside of file so it causes this infinite rendering problem
Instead remove getTagDetails and try to call this functions after API resolved.
useEffect(() => {
axios.get(URL).then((res) => {
const data = res.data;
getTagDetails(data)
});
}, []);
I think your problem is the way you have structured your getTagDetails function. Each time you render, you call getTagDetails() and the first thing you do is create a new array for each tag. When you call setTag with the new array, it will rerender. You'll probably want to move the getTagDetails logic into the effect so it only runs once on mount (or add a dependency to the dependency array if you need to update on new data)

Categories