I have the next problem:
export async function updateLessons() {
let data
await database().goOnline().then(async () => {
await database()
.ref('days')
.on('value', snapshot => {
console.log(snapshot.val())
data = snapshot.val();
});
});
return data;
}
I use this function to update my application when I swipe down
const onRefresh = React.useCallback(async () => {
setRefreshing(true);
setLessons(await updateLessons());
console.log(lessons)
setRefreshing(false);
}, []);
It is called from scroll view (refreshcontrol)
The problem is that it doesn't work asynchronously. In console log i see my snapshot. But the application updated faster and in console.log(lessons) it is undefined. How can I fix it?
Updating state is an asynchronous operation, and won't have completed yet by the time your console.log statement runs.
A simple way to get the correct output, is to capture the new lessons in local variable (which is updated synchronously):
const onRefresh = React.useCallback(async () => {
setRefreshing(true);
const newLessons = await updateLessons();
setLessons(newLessons);
console.log(newLessons)
setRefreshing(false);
}, []);
Also see:
The useState set method is not reflecting a change immediately, which also shows how to use a useEffect hook to respond to state changes.
React setState not updating state
Updated state values are not displaying in react
Related
I'm trying to get pinned article
const getCurrentlyPinned = async() =>{
setLoader(true)
await firestore()
.collection('admin_control')
.doc('currently_Pinned')
.get()
.then(snapshot =>{
const data = snapshot.data();
setpinnedNewsID(data.pinnedNewsId)
})
}
useEffect(() => {
getCurrentlyPinned().then(()=>{
console.log(pinnedNewsID)
})
}, [])
therefore calling it from useEffect and console logging it in .then function, but I'm getting its value as undefined. I dont know why I'm getting this.
That's a very common stuff, I guess there must be some similar question over stackoverflow but let me answer it for you. States are asynchronous so it takes a bit time of course to set it.
Just do the following stuff.
useEffect(() => {
getCurrentlyPinned()
}, [])
useEffect(() => {
console.log(pinnedNewsID)
}, [pinnedNewsID])
the state which you're setting in firebase function, just pass it in the dependency array of another useEffect so that you will console.log only when its value has been changed(or set)
async functions take some time to return data.
useEffect trying to access the data before it's ready. bc it runs immediately after the component renders. that's why pinnedNewsID value is Undefined.
do this instead: use 2 useEffect hooks.
useEffect(() => { getCurrentlyPinned() }, [])
useEffect(() => { console.log(pinnedNewsID) }, [pinnedNewsID])
I'm trying to save State twice, so I can reset it later on, but no matter what method I try, the 'setFullTrials' won't update with the saved data. The "console.log(savedData)" shows that all the data is there, so that's definitely not the problem. Not sure where I'm going wrong.
function AllTrials({Trialsprop}) {
let [savedData, setSavedData] = useState([]);
let [fullTrials, setFullTrials] = useState([]);
useEffect(() => {
//Call the Database (GET)
fetch("/trials")
.then(res => res.json())
.then(json => {
// upon success, update trials
console.log(json);
setFullTrials(json);
setSavedData(json);
})
.catch(error => {
// upon failure, show error message
});
}, []);
const resetState = () => {
setFullTrials(savedData);
//setFullTrials((state) => ({
...state,
savedData
}), console.log(fullTrials));
// setFullTrials(savedData.map(e => e));
console.log("savedData", savedData)
}
Setting the state in React acts like an async function.
Meaning that the when you set the state and put a console.log right after it, it will likely run before the state has actually finished updating.
Which is why we have useEffect, a built-in React hook that activates a callback when one of it's dependencies have changed.
Example:
useEffect(() => {
console.log(fullTrials)
// Whatever else we want to do after the state has been updated.
}, [fullTrials])
This console.log will run only after the state has finished changing and a render has occurred.
Note: "fullTrials" in the example is interchangeable with whatever other state piece you're dealing with.
Check the documentation for more info.
P.S: the correct syntax for useState is with const, not let.
i.e. - const [state, setState] = useState()
const [users, setUsers] = useState([]);
const getUsers = async () => {
const URL = "https://jsonplaceholder.typicode.com/users";
const response = await fetch(URL);
const data = await response.json();
console.log(data); // return 10 array .
setUsers(data);
console.log(users); // return empty Array
};
useEffect(() => {
getUsers();
}, []);
when I run this code and reload the browser, the data variable shows 10 arrays (that's what I expected) and the users state doesn't return an empty array (that's the problem). And then I'm changing something on code, and come to the browser, data variable and users state both shows 10 arrays.
That means I'm trying to tell you that, when I'm reloading the browser for the first time state doesn't set from data variable .
See the console result to better understand
Setting a state is an asynchronous process. That's why you won't see the immediate effect after calling the setUsers(). Rather you can add another useEffect in your code and see the changes happening for users
// ... other code
useEffect(() => {
// this will run after the 'users' has been changed
console.log("Users", users)
}, [users]);
// ...
After adding this useEffect you will see that the users will be printed after it is updated.
i have set one product details to setProduct use state in function fetchProduct().
const [ product, setProduct ] = useState({})
const fetchProduct = () => {
const apiURL = //api url;
fetch(apiURL)
.then((response) => response.json())
.then(res => {
setProduct(res.data[0])
})
.catch(error => {
console.error(error);
});
}
after i have call this function inside useEffect hook. and i have checked product is set or no in call with colsole log
useEffect(() => {
fetchProduct()
console.log(product)
return () => {
}
},[])
following result i have get in console
{}
then i have refreshed again result is showing in console. why in first time result is empty object. thanks for help
fetch product is asynchronous as is setState your console log executes before the data is retrieved and set
There are several behaviors for useEffect and it's dependency array:
In your example you use an empty array which acts as- componentDidMount and runs once.
that's why when you refresh your component it doesn't hold the default value anymore which is {}, but the value that was changed by your function.
In this case i'd add product state to the dependecy array which will cause the useEffect to execute the code inside the useEffect once on mount and whenever the
product state changes
for more https://reactjs.org/docs/hooks-effect.html
In your useEffect when javascript start executing
FetchProduct it continue to compile the next line it wont stop till the function execution finishes and then continue.
If you want to access product and do whatever you want with it you can use another use Effect right after yours.
useEffect(() => {
// Here you can access the product
console.log(product);
}, [product])
Edit: It just occurred to me that there's likely no need to reset the variable within the useEffect hook. In fact, stateTheCausesUseEffectToBeInvoked's actual value is likely inconsequential. It is, for all intents and purposes, simply a way of triggering useEffect.
Let's say I have a functional React component whose state I initialize using the useEffect hook. I make a call to a service. I retrieve some data. I commit that data to state. Cool. Now, let's say I, at a later time, interact with the same service, except that this time, rather than simply retrieving a list of results, I CREATE or DELETE a single result item, thus modifying the entire result set. I now wish to retrieve an updated copy of the list of data I retrieved earlier. At this point, I'd like to again trigger the useEffect hook I used to initialize my component's state, because I want to re-render the list, this time accounting for the newly-created result item.
const myComponent = () => {
const [items, setItems] = ([])
useEffect(() => {
const getSomeData = async () => {
try {
const response = await callToSomeService()
setItems(response.data)
setStateThatCausesUseEffectToBeInvoked(false)
} catch (error) {
// Handle error
console.log(error)
}
}
}, [stateThatCausesUseEffectToBeInvoked])
const createNewItem = async () => {
try {
const response = await callToSomeService()
setStateThatCausesUseEffectToBeInvoked(true)
} catch (error) {
// Handle error
console.log(error)
}
}
}
I hope the above makes sense.
The thing is that I want to reset stateThatCausesUseEffectToBeInvoked to false WITHOUT forcing a re-render. (Currently, I end up calling the service twice--once for win stateThatCausesUseEffectToBeInvoked is set to true then again when it is reset to false within the context of the useEffect hook. This variable exists solely for the purpose of triggering useEffect and sparing me the need to elsewhere make the selfsame service request that I make within useEffect.
Does anyone know how this might be accomplished?
There are a few things you could do to achieve a behavior similar to what you described:
Change stateThatCausesUseEffectToBeInvoked to a number
If you change stateThatCausesUseEffectToBeInvoked to a number, you don't need to reset it after use and can just keep incrementing it to trigger the effect.
useEffect(() => {
// ...
}, [stateThatCausesUseEffectToBeInvoked]);
setStateThatCausesUseEffectToBeInvoked(n => n+1); // Trigger useEffect
Add a condition to the useEffect
Instead of actually changing any logic outside, you could just adjust your useEffect-body to only run if stateThatCausesUseEffectToBeInvoked is true.
This will still trigger the useEffect but jump right out and not cause any unnecessary requests or rerenders.
useEffect(() => {
if (stateThatCausesUseEffectToBeInvoked === true) {
// ...
}
}, [stateThatCausesUseEffectToBeInvoked]);
Assuming that 1) by const [items, setItems] = ([]) you mean const [items, setItems] = useState([]), and 2) that you simply want to reflect the latest data after a call to the API:
When the state of the component is updated, it re-renders on it's own. No need for stateThatCausesUseEffectToBeInvoked:
const myComponent = () => {
const [ items, setItems ] = useState( [] )
const getSomeData = async () => {
try {
const response = await callToSomeService1()
// When response (data) is received, state is updated (setItems)
// When state is updated, the component re-renders on its own
setItems( response.data )
} catch ( error ) {
console.log( error )
}
}
useEffect( () => {
// Call the GET function once ititially, to populate the state (items)
getSomeData()
// use [] to run this only on component mount (initially)
}, [] )
const createNewItem = async () => {
try {
const response = await callToSomeService2()
// Call the POST function to create the item
// When response is received (e.g. is OK), call the GET function
// to ask for all items again.
getSomeData()
} catch ( error ) {
console.log( error )
}
} }
However, instead of getting all items after every action, you could change your array locally, so if the create (POST) response.data is the newly created item, you can add it to items (create a new array that includes it).