Array doesn't update after adding new item, using React Hooks - javascript

I have an array that shows a set of items attached to a user. The array checks it first if the current user is on the item's inventory and then displayed as 'user name' and 'date borrowed' on a table. The adding feature is done in a modal, and suppose to update the table.
The problem is everytime I add, delete or update, the table doesn't update at all. Also this table is an expandend component of another table (react-data-table-component)
Here is the useState, and useEffect of my table:
const InventoryTable= ({
selectedUser,
items,
getItems,
getUsers
}) => {
useEffect(() => {
getItems();
getUsers();
}, []);
const [data, setData] = useState([]);
useEffect(() => {
let data= [];
data= items?.filter((item) =>
item?.users.some(
(user) => parseInt(user?.id) === parseInt(selectedUser?._id)
)
);
setData(data);
}, []);
Note: selectedUser, is the user from the main table that was selected and this current table is to show the itms attached to it.
If I add data on the setData(data); }, []); it crashes.
Adding data, selectedUser, and items on the dependency arrays loads it non-stop that causes to crash the page

useEffect method takes 2 parameters as input.
callback
dependency
If the dependency is empty array it will be called in the similar way as Class Component with componentDidMount and componentWillUnmount lifecycle methods.
if dependency is present then the callback will be called after the UI in painted.
So clearly you have missed required dependency

I'm not sure that i understand the whole concepts of your code because it is just a small part of the code. But useEffect() will run accordingly if you want it to run as ComponenentDidMount you will use the code that you said above however in your case you want to update delete add it means you want to detect the change in data so you need to include data within the brackets like this
`useEffect(() => {
let data= [];
data= items?.filter((item) =>
item?.users.some(
(user) => parseInt(user?.id) === parseInt(selectedUser?._id)
)
);
setData(data);
}, [data,items,selectedUser]);`

Seems you forgot to pass items and selectedUser to effect dependency array.

Maybe I am wrong, but have you tried renaming data property inside useEffect? It should have problem, that you are setting state without set callback.
Try also set useState default value to [] instead of {} (object).

Related

React useState doesn't update even with useEffect added

Probably it is a classic issue with useState which is not updating.
So there is a tree with some checkboxes, some of them are already checked as they map some data from an endpoint.
The user has the possibility to check/uncheck them. There is a "cancel" button that should reset them to the original form.
Here is the code:
const [originalValues, setOriginalValues] = useState<string[]>([]);
...
const handleCancel = () => {
const originalValues = myData || []; //myData is the original data stored in a const
setOriginalValues(() => [...myData]);
};
...
useEffect(() => {
setOriginalValues(originalValues);
}, [originalValues]);
However, it is not working, the tree is not updating as it should. Is it something wrong here?
Just do the following, no need for ()=> the state will update inside the hook if called, plus change the constant it will cause confusion inside your code and protentional name clash later on, with the current state variable name, and also make sure your data are there and you are not injection empty array !!!! which could be the case as well !.
// Make sure data are available
console.log(myData)
// Then change the state
setOriginalValues([...myData]);

How do i re-run useEffect hook if the localstorage value has been changed?

I'm trying to re-run the useEffect hook based if any new item has been added to the localstorage.
My useEffect looks like this :
useEffect(() => {
//getting all localstorage items and setting to a variable
const localStorageItems = { ...localStorage }
// filtering out the localstorage items for keys that only starts with "HTTP" & pushing it to the state called "testData" using setTestData useState hook.
const filteredByKey = Object.fromEntries(
Object.entries(localStorageItems).filter(([key, value]) => {
if (key.startsWith("http")) {
testArr2.push({ urls: [key, value] })
// setTestData({ urls: [key, value] })
setTestData((prev) => [...prev, { key, value }])
}
})
)
}, [])
My problem is when I hit a button(adding a new data) this useEffect should ideally re-run, setting the latest data to the "testData" state.
The Problem :
I cannot pass window.localstorage to the useEffect's array dependency list, as it is external variable I believe and react doesn't allow it.
What I have tried:
I have looked at this answer on SO, but my situation is a bit complex as I'm loading the entire items to a variable first(I'm not aware of any better alternatives)
I have tried to put the state value in the dependency list, but it doesn't work and goes to infinite loop.
Please help.
Thanks for reading this far.
i can give you a work around for this use case, create react context that syncs with localStorage. you can use the context as a useEffect dependency. sync means, take the value from localStorage at starting and update the context along with the localStorage from your app.
Define a state that holds the local storage value and pass it as a useEffect parameter.
const [localStorageData, setLocalStorageData] = useState([]);
useEffect(() => {
// Your codes
}, [localStorageData])
you can use 'storage' event listener
window.addEventListener('storage', function(e) {
if (e.newValue) {
console.log('new data is saved ...')
// ...... your code
}
})

is a good approach defining 2 useEffect Hooks, see my use of case bellow

I tried to find the same case, but everyone is in a different situation.
I've created 2 useEffects in my component, one for initializing the component (1 execution) and another to fetch data when some state (filter) changes. (many executions)
const [filter1, setFilter1] = useState({});
const [filter2, setFilter2] = useState({});
//initialize filters, only once
useEffect(() => {
//here i fetch all the data to fill the filter components.
setFilter1(data1);
setFilter2(data2);
}, []);
//everytime my filters change.
useEffect(() => {
//here i fetch data using the filter as parameters.
}, [filter1,filter2]);
The code above is my last approach and works fine. Every time I change the filters the useEffect fetch the data and load the table with that data.
I show below the old approach that I took, and the one I didn't like.
const loadData = useCallback((filter1,filter2) => {
//here i fetch data using the filter as parameters.
}, [filter1,filter2]);
useEffect(() => {
//here i fetch all the data to fill the filter components.
setFilter1(data1);
setFilter2(data2);
loadData(filter1, filter2);
},[]);
and I used the loadData function in every filter change handler like:
const onFilter1Change = (val) => {
setFilter1(val);
loadData(val, filter2)
};
The old approach above brought me some issues regarding how to use the hooks properly and that's why I find myself asking you React experts, how to improve my coding in the best way.
The new approach works fine for me, tho I wonder if it's right to use 2 useEffect in this case, or if there is a better way to do it.
Cheers
Where data1 & data2 comes from?
If it's a prop, and you are using a useEffect only to initialize them, you don't need a useEffect for that:
const [filter1, setFilter1] = useState(data1);
const [filter2, setFilter2] = useState(data2);
useEffect(() => {
//here i fetch data using the filter as parameters.
}, [filter1, filter2]);

added data don't update automatically in useEffect() Need refresh browser to get the last addition

I have a trouble in small app. I have one component that add data to db. Works well. In other component I fetch the data inside useEffect() and make a search from db. As well works well. The two components are returned in my App.js. The problem is that when I add data and go to search I need to refresh the page for search the last adding. So if I don't refresh the page it doesn't find it. How to solve this? (a bit explained if possible) :)
From component Search.js
useEffect(()=>{
axios.get("http://localhost:3004/api/list/").then(resp=>{
console.log(resp.data)
setList(resp.data) ;
})
},[])
From component
const AddData = () =>{
const [name, setName] = useState();
const [tel, setTel] = useState();
const [gender, setGender] = useState();
const dispatch = useDispatch();
const handleSubmit = (e) => {
e.preventDefault();
// starting to work to save input to DB via API
// dispatch(addList(name,tel,gender));
dispatch(addList(name,tel,gender))
}
return(
```
Because of the empty dependency array, useEffect runs only once like componentDidUpdate.
The dependency array can't be empty, you either put something in there or get rid of the dependency array.
I fixed. I deleted the useEffect() and create a function with the same Axios getting list. I call the function when typing the search. It works. Thx guys for your answers. They were very welcome.
If you put your search as state e.g.
const{query, setQuery} = useState('')
Then when you search setQuery('my-search-term')
Then useEffect but add state to the dependency array so every time that bit of state is updated it fires the axios request
useEffect(()=>{
axios.get("http://localhost:3004/api/list/").then(resp=>{
console.log(resp.data)
setList(resp.data) ;
})
},[query])

Can not update the list of items?

My list of items is not being updated when I do a delete of a row even though the console.log(data) results in one less item after deleting the row, but the console.log('list of items after: ', listOfitems) gives me all full items even those that are deleted.
const [listOfitems, setListOfitems] = useState([]);
useEffect(() => {
getListOfitems();
}, [])
Please let me know what could be possibly wrong here:
editable={{
onRowDelete: (oldData) =>
new Promise((resolve, reject) => {
setTimeout(() => {
{
console.log('list of items before: ', listOfitems)
console.log('oldData.item_key;: ', oldData.item_key)
const indexToDelete = oldData.item_key;
console.log(indexToDelete)
const data =
listOfitems.filter(
(item) => ((item.item_key !== oldData.item_key))
)
setListOfitems(data);
console.log(data)
console.log(setListOfitems(data))
deleteitem(oldData);
setListOfitems(data);
console.log('list of items after: ', listOfitems)
}
resolve();
}, 1000);
}),
}}
The second argument in the useEffect hook is an array of values that the hook depends on. In your example you passed an empty array, which is great for making the hook work like ComponentDidMount(). However, since your hook now depends on no values, it will never fire again. In the below example, passing listOfItems into that array will cause the hook to fire whenever that piece of state changes. Not seeing the rest of your code, but based on your description of the problem, this is my best guess as to a solution for you.
Hope this helps.
const [listOfitems, setListOfitems] = useState([]);
useEffect(() => {
console.log('useEffect')
getListOfitems().then((listOfitems) => {
setListOfitems(listOfitems);
});
getDocuments();
}, [listOfItems]);
Every time the component is rendered a new const state is assigned to useState hook. In you case every time your component is rerendred this line runs assigning a new value to the listOfitems.
const [listOfitems, setListOfitems] = useState([]);
This means that console logging out the state before the component rerenders would return the old state.
Console.log(data) results in the correct state because you simply filter and return a new array.
As dvfleet413 mentioned you do not include the listOfItems in the argument array of the useEffect meaning that your component only renders once.
The set* methods for state are asynchronous, so if you try to access listOfitems immediately after calling setListOfitems, you could be using the old value and not the newly updated one.
You could use a separate useEffect method that is dependent on the value being set in the first one (use the second param like [listOfitems]). This other method should get triggered after the update.
In short - the data IS being updated, but your console.log is displaying the old value before the update is complete.
edit: Code example:
useEffect(()=>{
getListOfitems()
// console.log here may NOT show the new listOfitems value because
// setListOfitems is asynchronous, and getListOfitems may also be
}, []) // only loads on the initial mount
I assume getListOfitems will retrieve the data from an external source initially, and calls setListOfitems inside it. The code in the post does not show what is happening.
A second useEffect could look like:
useEffect(()=>{
// do something with listOfitems
}, [listOfitems]) // gets called every time listOfitems is updated

Categories

Resources