React Native, pushing items to an array state not working - javascript

I need to push items to my array, but when I console.log this array, it says 'undefined' for the specific value. I'm trying btw to fetch data from firebase storage. How do I correctly add items to an Array
Here is my code:
const [imagelinks, setImagelinks] = React.useState(['']);
const myFunction = () =>{
await storage()
.ref(`${_userid}`)
.list()
.then(result => {
result.items.forEach(async ref => {
await storage()
.ref(ref.fullPath)
.getDownloadURL()
.then(url => {
//get url
setImagelinks([...imagelinks, url]);
console.log('Links: ' + url);
});
});
//there it says undefined when logging...
console.log(imagelinks[0])
});
}
Edit: can I use following?
const imagelinks = [];
//instead of
const [imagelinks, setImagelinks] = useState([]);

Updating state is an asynchronous task. A re-render of the component is required in order to have the updated value. To be sure, add your console outside of myFunction, below the state definition for example:
const [imagelinks, setImagelinks] = React.useState(['']);
console.log(imagelinks)
If you wanna use the result to do some logic (an API call for example), you can use the useEffect hook, like this for example:
useEffect(()=>{
if(imagelinks.length>0){
// do your things, except do not call setImagelinks in here to avoid having a loop because imagelinks is in the dependencies array of useEffect.
}
},[imagelinks])

As the previous answer mentioned, you can't access the new state in the same scope after setting it as it will still have the old value for that specific scope.
If you really need to store and access some value in the same scope you can use useRef hook or you can store the value in a variable in that scope.
Here is an example, but keep in mind that changing the referenced value will not trigger a re-render so this will not replace your useState.
variable example:
let imageLinksArray=[...imagelinks, url] //add this in your function
console.log(imageLinksArray) //you can pass this to the next function
useRef example:
const imagelinksRef = useRef() //add this on top after your useState
...
imageLinksRef.current=[...imagelinks, url] //add this in your function
console.log(imageLinksRef.current) //this will give you the result you're expecting
you can also check out this npm package that let's you access the new state through a ref directly after setting it (if you really need to)
https://www.npmjs.com/package/react-usestateref

Related

how to map array result from console.log into table in react js

I have an array of results coming from an API that I called using axios. get(). Then the result is in array and I want to map it into table in react js.
Below is the API fetching data
const [data, getData] = useState([])
useEffect(() => {
axios.get("http://localhost:4000/api/authentication/history")
.then(result =>{
console.log(result.data.result)
getData(JSON.stringify(result.data.result))
console.log(data.length)
})
}, [])
The line console.log(data.length) returns 0. I dont know why the data is not stored in the data function. That is why when I map {data.map} into it will return error as data.map is not a function. So, is there a simpler way to display and map this array of result into react js table?
Here is the image of the array of result (it is not in json type but in array)
Update #1
Since posting this (7 hours ago), I tried to do this myself, and realised I completely invented this feature, and callback isn't actually a function of useState
I did some research and came across this very useful function:
const useStateCallback = (initialState) => {
const [state, setState] = useState(initialState);
const cbRef = useRef(null); // init mutable ref container for callbacks
const setStateCallback = useCallback((s, cb) => {
cbRef.current = cb; // store current, passed callback in ref
setState(s);
}, []); // keep object reference stable, exactly like `useState`
useEffect(() => {
// cb.current is `null` on initial render,
// so we only invoke callback on state *updates*
if (cbRef.current) {
cbRef.current(state);
cbRef.current = null; // reset callback after execution
}
}, [state]);
return [state, setStateCallback];
};
Original
I don't know what getData is, but I'll assume it's state, and you have something like this:
const [data, getData] = useStateCallback(); // previously useState
If that's the case, when you call your getData, you can do a callback as the 2nd argument. That callback happens when your state updates successfully:
...
getData(JSON.stringify(result.data.result), () => console.log(data.length));
...
If that's now how you're doing it, then I'd suggest you change whatever you're doing to be in state, and also rename getData to setData
Explanation
When you're calling your getData, you're telling react that you want to update your data? Great! The thing to note is, react doesn't do this update immediately. Instead, it updates it (and other state) in the future, all at the same time.
With that in mind, you pass the callback function as the 2nd argument to tell react what you want to happen once this specific state has been updated
As for getData to setData? That's because the function doesn't return (get) anything, but does set something. Makes your code clearer

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
}
})

Do functions get the latest state value in React?

I have a function inside of my functional component that uses a value saved in state. However, when it is called, it has the original value in state, not the updated value. When I look at my component in Chrome React Dev Tools, I see that the updated value is stored in state. Aren't functions supposed to get the latest state value in React? I didn't think I'd have to wrap my functions in a useEffect every time some value in state they depend on changes. Why is this happening?
const Editor = (props) => {
const [template, setTemplate] = useState(null);
const [openDialog, setOpenDialog] = useState(false);
useEffect(() => {
if (props.templateId) {
getTemplate(props.templateId));
}
},[]);
const getTemplate = (templateId) => {
{...make API to get template...}
.then((response) => {
if (response.template) setTemplate(response.template);
});
}
/* THIS FUNCTION SAYS TEMPLATE IS ALWAYS NULL */
const sendClick = async () => {
if (template) {
await updateTemplate();
} else {
await initializeTemplate();
}
setOpenDialog(true);
};
}
UPDATE: I figured out the issue. The sendClick function is being used inside an object that I have in state. When that object is created, it creates a version of the sendClick function based on the state at that time. I realized I needed to refactor my code so that the function is not stored within my object in state so that the function will always have the latest state values.
Please correct the code there its setTemplate(template)); not getTemplate(template));
I'm guessing that you have that right in the source code... if Yes then,
You have got into a trap that all developers new to React fall into.
This code is betraying you ...
useEffect(() => {
if (props.template) {
setTemplate(template)); // Mentioned as getTemplate(template));
}
},[]); // Here is where you make the mistake
The second argument you pass to the useEffect is called as Dependencies. Meaning if your useEffect is dependent on any state or any variable or function, Ii should be pass as the second argument inside the []. By now you should have got the answer.
Clearly, your useEffect is dependent on template. You should pass that inside the [].
So the code will be : -
useEffect(() => {
if (props.template) {
setTemplate(template)); // Mentioned as getTemplate(template));
}
},[template]);
Now React will automatically run the function every time the value of template changes therefore, updates template.
For more information about useEffect ...
Refer React Documentation
Refer the useEffect API

React Hooks: Adding new fields to an Object state does not get reflected immediately

I am using React Hooks to manage states within a component.
const addNode = () => {
let pform = pForm
let handles = [vForm, yForm, hForm]
let access_info = [virtualForm, management1Form, management2Form, consoleForm]
let newObj = {
...currentForm,
p: pform,
handles: handles,
access_info: access_info,
}
console.log('newObj', newObj)
setCurrentForm(
newRouterObj
)
console.log(currentForm)
let currArr = [...addedNodes]
currArr.push(currentForm)
setAddedNodes(currArr)
intializeForms()
}
The function above is an onClick that I use when I press an Add button. The forms (pForm, vForm, yForm, etc.) are all separate states. I gather them together and put them into a single object newObj and use setCurrentForm to update the currentForm state to newObj.
When I console.log the newObj, everything goes in fine. However, when I check the currentForm after the setCurrentForm, the fields (p, handles, and access_info) are empty.
I know that states in React can have a delay in updates so I might have to use useEffect. However, in my use case, which is to gather different states and put them in as a new field in the currentForm state seems useEffect is not the best way to solve it. Can anyone help please?
You are misunderstanding exactly how useState works. When you call the useState setter function, the state value isn't actually updated immediately, instead it will trigger the component to re-render with the updated value. Even though you call the setter half way through the function, the state value will remain the original value for the entire lifetime of that function call.
You could slightly tweak what you have to be
const addNode = () => {
...
let currArr = [...addedNodes]
// you know that currentForm is supposed to be newObj, so just push that
// see my explanation above to understand why it currentForm isn't what you expect
currArr.push(newObj)
...
}
It's an async action so values will not be assigned/updated instantly. You need to watch for the changes using useEffect hook to log new values and to do anything in case
useEffect(() => {
// Whenever `currentForm` will be updated, this callback will be invoked
console.log('updated currentForm values', currentForm);
},[currentForm]);

Categories

Resources