useState is not updating state immediately - javascript

here userlist is updating immediately what can be correct code for above logic
I am trying fetch userlist from firestore than traversing that list to find user details from different collection
useEffect(() => {
db.collection("following/" + Credens.uid + "/userFollowing")
.get()
.then((snapshot) => {
followingList = snapshot.docs.map((value, ind) => value.data());
})
.then(() => {
if (followingList.length > 0) {
followingList.map((value, index) => {
db.collection("Postss")
.doc(value.useruid)
.collection("MovieWatched")
.get()
.then((snaps) => {
// let Detail = snap.data()
let movieidList = snaps.docs.map(
(value) => value.data().postMovieId
);
if (movieidList.includes(MovieId) === true) {
setuserList((prev) => [...prev, value.useruid]);
}
});
});
}
})
.then(() => {
console.log(userList);
userList.map((value, index) => {
db.collection("users")
.doc(value)
.get()
.then((snapshot) => {
setfriendsWatchedData((prev) => [
...prev,
{
usersID: value,
userData: snapshot.data(),
},
]);
});
});
});
// return () => {
// cleanup
// }
}, []);

To be sure the state did change, you can use the useEffect() to monitor the changing of that state like:
useEffect(() => {
if (userList) {
// userList.map....
}
}, [userList])
Additional conditions can be specified in the if statement. The hook will run whenever the state changes.

Related

after updated array list not index instantly -react.js

i need a filter with time "old to new" and "new to old"
here is my code template:
const timeNewToOld = () => {
const [paginationUsers,setPaginationUsers] = useState([])
const newToOld = users.sort((a, b) => {
return b.Time.localeCompare(a.Time)
})
setPaginationUsers(newToOld)
}
const timeOldToNew = () => {
const oldToNew = users.sort((a, b) => {
return a.Time.localeCompare(b.Time)
})
setPaginationUsers(oldToNew)
}
this functions working but, not responding instantly on web browser.
i hope i can explain with these images:
i click on the "newtoold" function and nothing changes:
i move to the next page and i'm back to the 1st page:
everything is fine. only the first time I click on the function, it doesn't get instant updates, when I change the page, the index returns to normal.
paginationUsers created here:
useEffect(() => {
const getAllData = async () => {
onSnapshot(_dbRef, (snapshot) => {
const data = snapshot.docs.map((doc) => {
return {
id: doc.id,
...doc.data(),
}
})
setUsers(data)
setUserPageCount(Math.ceil(data.length / 20))
})
}
getAllData()
}, [])
useEffect(() => {
displayUsers(users, setPaginationUsers, userCurrentPage)
}, [users, setPaginationUsers, userCurrentPage])
i hope i could explain,
happy coding..
Array.prototype.sort doesn't create a new array, so react can't know that it changed. Creating a new array should help.
const timeOldToNew = () => {
const oldToNew = [...users].sort((a, b) => {
return a.Time.localeCompare(b.Time)
})
setPaginationUsers(oldToNew)
}

Firestore get subcollection data with promises

I'm trying to get the data from my subcollections, the things is I need to do it with Promises (If I don't, I can't get the data from the cache)
Here how I am actually doing :
bookStores = db.collection("bookstores");
bookStores.onSnapshot((snapshot) => {
snapshot.docChanges().forEach((change) => {
bookStoresIds.push(change.doc.id); // I use a list so a can iterate on IDs for subs
// Doing stuff
});
bookStoresIds.forEach(bookStoreId => {
const task = db.collection('bookstores').doc(bookStoreId).collection('books')
task.onSnapshot((snapshotTask) => {
snapshotTask.docChanges().forEach((change) => {
// Doing stuff
});
});
})
I use a list to store the IDs. This version works, but causes me some troubles and I want to use Promises.
Here what I tried :
async function getBookStores(id,) {
const bookStoreIds: string[] = [];
db.collection("bookStores").onSnapshot({ includeMetadataChanges: true }, (snapshot) => {
snapshot.docChanges().forEach((change) => {
// Doing Stuff
});
});
return bookStoreIds;
}
async function getBooks(bookStoreIds) {
bookStoreIds.forEach(bookStoreId => {
const book = db.collection('bookStores').doc(bookStoreId).collection('books')
task.onSnapshot({ includeMetadataChanges: true }, (snapshotTask) => {
snapshotTask.docChanges().forEach((change) => {
// Doing Stuff
});
})
})
}
getBookStores(id)
.then((list) => {
return getBooks(list);
})
The problem is, when it cames to getBooks, the list is empty ... Is somebody have an idea ? 🙏

React wait for one loop (and setState) to finish before another loop

handleSubmit = () => {
this.setState({ modalState: false })
this.state.codeToClass.forEach((code, classId, map) => {
const cr = _.find(this.state.classRoles, { id: classId })
if (code === cr.classCode) {
console.log('here')
this.setState(state => ({
classRoles: state.classRoles.map((cc) => {
console.log(cc.id)
console.log(classId)
console.log(cc.id === classId)
if (cc.id === classId) {
console.log('here1')
return {
...cc,
role: 'TA',
}
}
console.log('what')
return cc
}),
}), ()=> console.log(this.state.classRoles)) //this is called later
} else {
NotificationManager.error('Failed to register as TA.')
}
})
console.log(this.state.classRoles) //this is called first
this.state.classRoles.forEach((c) => {
if (c.role === '') {
api.deleteClassUser(c.id, this.state.user.id)
} else {
api.postAddClass(c.id, this.state.user.id, c.role)
console.log(c)
}
})
EventEmitter.publish('currentlyEnrolled', this.state.classRoles)
}
I'm trying to run the second forEach after the first forEach has finished,i.e. state has been updated. But it keeps running in this order. Is there a way to fix this?
Promise is your friend.
// You map your operations in to Promises.
const promises = this.state.codeToClass.map((code, classId, map) => {
return new Promise(resolve=>{
const cr = _.find(this.state.classRoles, { id: classId })
if (code === cr.classCode) {
console.log('here')
this.setState(state => ({
classRoles: state.classRoles.map((cc) => {
console.log(cc.id)
console.log(classId)
console.log(cc.id === classId)
if (cc.id === classId) {
console.log('here1')
return {
...cc,
role: 'TA',
}
}
console.log('what')
return cc
}),
}), ()=> resolve()) //this is called later
} else {
NotificationManager.error('Failed to register as TA.')
}
})
})
// and then you wait for all of the promises
await Promise.All(promises);
// then continue to execution
There are two options.
Use Promise
Async await
Since map can be used with await, I think
const tempState = this.state.codeToclass;
await tempState.map(...
This way can work :)
this.setState is an asynchronous operation.
You can try something like this:
handleSubmit = () => {
//some code...
this.setState(state => ({
state.codeToClass.forEach((...args) => {
//logic to update the state...
});
}), setClassRoles); //call a function after the state value has updated
}
setClassRoles = () => {
this.state.classRoles.forEach((...args) => {
//your code...
});
EventEmitter.publish('currentlyEnrolled', this.state.classRoles)
}

how to remove listener on cleanup function

how can I remove the listener in this case when id is not defined? the cleanup is calling getMessages.off() but this is not defined.
useEffect(() => {
if (id) {
const getMessages = database
.on("child_added", function(snapshot) {
console.log(snapshot)
});
}
return () => {
getMessages.off();
};
}, [id]);
Define getMessages before if statement. This will be accessible in closure function for clean up.
useEffect(() => {
let getMessages;
if (id) {
getMessages = database.on("child_added", function (snapshot) {
console.log(snapshot);
});
}
return () => {
getMessages && getMessages.off();
};
}, [id]);
I don't recommend calling cleanups in effects with deps because this will lead to unexpected behavior, meaning it will get called earlier than expected.
You can save your cleanup in a ref, update it when id changes in another effect and call it in an effect with no deps.
const getMessages = useRef();
useEffect(() => {
return () => {
getMessages?.current?.off();
};
}, []);
useEffect(() => {
if (id) {
getMessages.current = database
.on("child_added", function(snapshot) {
console.log(snapshot)
});
}
}, [id]);

Why is State always empty?

I have thoroughly gone through all the asked question and none of them apply to my problem directly. I am looping through an array of user ids and matching them to get a user from my firestore db. I get the result back with no problem but when i store it in the state array and run a console log, my state array is always empty. The first console.log works and shows the results from the db.
Here's my code:
const UsersScreen = (props) => {
const [state, setState] = useState({
users: []
});
const getUserProfiles = () => {
let users = [];
//networkUsers is an array with the ids
networkUsers.forEach(userId => {
db.doc(userId).get().then((doc) => {
users.push(doc.data());
console.log('localusers', users)
}).catch((error) => {
console.log('caught error', error)
})
});
setState({ users: users });
};
useEffect(() => {
getUserProfiles();
}, []);
console.log('state', state.users)
}
Please help.
The logic that fetches the document from Firestore is asynchronous. The call to setState is synchronous though. It will always before the document has been fetched. The solution would be to fetch the documents then set the state. Here is an example:
const UsersScreen = (props) => {
const [state, setState] = useState({
users: [],
});
const getUserProfiles = () => {
Promise.all(networkUsers.map((userID) => db.doc(userId).get()))
.then((docs) => {
setState({ users: docs.map((doc) => doc.data()) });
})
.catch((err) => {
console.log("caught error", error);
});
};
useEffect(() => {
getUserProfiles();
}, []);
console.log("state", state.users);
};
The Promise.all call resolves once every user has been fetched from the Firestore (maybe you could fetch them at once though). Once we have the users we loop over them with map to extract the data of the document and set the state. Here is an alternative with async/await:
const UsersScreen = (props) => {
const [state, setState] = useState({
users: [],
});
const getUserProfiles = async () => {
try {
const docs = await Promise.all(
networkUsers.map((userID) => db.doc(userId).get())
);
setState({ users: docs.map((doc) => doc.data()) });
} catch (err) {
console.log("caught error", error);
}
};
useEffect(() => {
getUserProfiles();
}, []);
console.log("state", state.users);
};

Categories