This question already has answers here:
Using async/await with a forEach loop
(33 answers)
console.log of element.children shows 0 length but has three entries when expanded later
(1 answer)
Closed 1 year ago.
I am using Firebase in this component to retrieve an array of some basic user details. The array size is always 3 with this query. The function that carries out this action is whoToFollow() and when the results are received the function updates a state variable 'who' There is no array mutation happening here. I simply reset the state to the returned value.
Unfortunately despite confirming that the state has indeed been updated, the component does not re-render to display this. In order to simplify the code I have only used the length of the array as the value to be displayed.
Below the code I have included a screenshot of the console logs which display the code execution steps and shows a confirmation of the state value being changed. (returned data is made up test data)
const storage = getStorage(firebaseApp);
const db = getFirestore();
function Aside() {
const [who, setWho] = useState([])
const whoToFollow = async () => {
const usersRef = collection(db, "users");
const q = query(usersRef, limit(3));
const querySnapshot = await getDocs(q);
const results = await querySnapshot.docs.map(doc => doc.data());
let filteredArray = []
results.forEach(async (user) => {
let imgPath = await getDownloadURL(ref(storage, user.profilePicture));
let tmpObj = {
uid: user.UID,
displayName: user.displayName,
userName: user.userName,
profilePicture: imgPath
};
filteredArray.push(tmpObj);
});
console.log('Users recieved and updating who state')
setWho(filteredArray)
}
useEffect(() => {
console.log('component did mount, calling "WhoToFollow()" function')
whoToFollow()
}, []);
useEffect(() => {
console.log('who state has been updated but nothing is being rendered?')
console.log(who)
}, [who]);
return (
<aside>
<div className='aside-head'>Search</div>
<div className='aside-cont'>
<div className='who-to-follow'>
<div className='wtf-head'>
New users to follow
</div>
<div className='wtf-list'>
{who.length}
</div>
</div>
</div>
</aside>
);
}
export default Aside;
Related
I am currently using expo react-native to create a feed in which all user posts could be retrieved, I have come up with a solution to input all of the current users posts
const Posts = firebase.firestore().collection("posts").doc(user.uid).collection("userPosts");
const [users, setUsers] = useState([]);
useEffect(async () =>{
Posts
.onSnapshot(
querySnapshot => {
const users = []
querySnapshot.forEach((doc) => {
const { caption, creation, DownloadURL} = doc.data()
users.push({
id: doc.id,
caption,
creation,
DownloadURL,
})
})
setUsers(users)
}
)
}, [])
but I could not find a way to get retrieve all the UID's of all the users with posts in order for me to access all the posts, my database goes as follows:
posts(base collection with UID's of users)->userPosts( with UIDS of each post and fields for posts )
For reference I have also tried using the same format but to no avail as I tried to pass the values and it led to 'undefined', closest I have gone is get this on the console log:
The code for the above is here:
const AllUids = firebase.firestore().collection("posts");
const [uids, setUids] = useState([]);
useEffect(async () =>{
AllUids
.onSnapshot(
querySnapshot => {
const uids = []
querySnapshot.forEach((doc) => {
const {id} = doc.data()
uids.push({
id: doc.id,
id
})
})
setUids(uids)
}
)
}, [])
console.log(uids)
Both loading data from Firestore and setting modifying the state in a React app are asynchronous operations, so by the time your console.log(uids) runs neither of those will have completed yet.
The easiest way to log the uid value, is to use another effect that depends on that state variable:
useEffect(() => {
console.log(uids);
}, [uids]);
This has been coming up repeatedly in the past days, but I'm not sure why folks are suddenly bumping into this more than before.
This question already has answers here:
The useState set method is not reflecting a change immediately
(15 answers)
Closed 11 months ago.
I have a simple page editor, When a user clicks edit page it opens an editor. I am passing the ID of the page using redux which will be used to get data from API.
Here is my Editor.
const [pageData, setPageData] = useState("");
const getPage = async (id) => {
try {
const response = await api.get(`/landing_pages/${id}`);
console.log("page", response.data); // displays data at the end
setPageData(response.data);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
getPage(pageID);
console.log('Page Data', pageData) // displays nothing
let LandingPage = pageData;
const editor = grapesjs.init({
container: "#editor",
components: LandingPage.components || LandingPage.html,
})
}, [pageID, getPage])
Why is Page Data display nothing even though the data from API is returned and is displayed in the console at the end? what am I doing wrong here?
Even if you await your getPage call, the updated pageData won't be available until the next render cycle so your assignment to LandingPage will be one cycle behind.
You should instead update in one useEffect and watch for changes to pageData in another.
const [pageData, setPageData] = useState("");
useEffect(() => {
const getPage = async (id) => {
try {
const response = await api.get(`/landing_pages/${id}`);
console.log("page", response.data); // displays data at the end
setPageData(response.data);
} catch (error) {
console.log(error);
}
};
getPage(pageID);
}, [pageID]);
useEffect(() => {
console.log('Page Data', pageData); // displays updated pageData
let LandingPage = pageData;
const editor = grapesjs.init({
container: "#editor",
components: LandingPage.components || LandingPage.html,
});
}, [pageData]);
Im trying to just use a empty array in a react/ts project like this.
const [companyChatrooms, setCompanyChatrooms]: any = useState([]);
I then use a useEffect to get it done when rendering component.
async function fetchMyChatRooms() {
const userCollection = await firestore.collection('user_in_chat')
const snapshot = await userCollection.where('user_id', '==', myIdNumber).where('chatroom_id', '==', companyChatrooms).get();
snapshot.forEach(doc => {
const roomID = doc.data().chatroom_id
setMyChatrooms([...myChatrooms, roomID])
});
}
fetchMyChatRooms()
}, [companyChatrooms, myIdNumber])
console.log(myChatrooms)```
However, my console.log shows 2 arrays with each value instead of 1 array holding both values.
How can i make sure both values are stored in same array?
[1]: https://i.stack.imgur.com/q0WPD.png <-- Check how the output looks.
I assume you have an array snapshot with more than 1 element and any iteration you are updating the state. This caused multiple re-render
I suggest you to update state after iterate entire array. Example:
const rooms = []
snapshot.forEach(doc => {
const roomID = doc.data().chatroom_id;
rooms.push(roomID);
});
setMyChatrooms(rooms)
you should set all of them in one time.
async function fetchMyChatRooms() {
const userCollection = await firestore.collection('user_in_chat')
const snapshot = await userCollection.where('user_id', '==', myIdNumber).where('chatroom_id', '==', companyChatrooms).get();
// here is the changing
const roomIDs = snapshot.map(doc => doc.data().chatroom_id);
setMyChatrooms(roomIDs )
//
fetchMyChatRooms()
}, [companyChatrooms, myIdNumber])
console.log(myChatrooms)
Started playing around with createAsyncThunk for learning purpose, decided to implement a shopping cart with firebase firestore but I ran into problems when trying to implement pagination in my react app.
How should I return the last visible state into my redux state during the initial load and subsequent load (infinite loading)
I am basing on code from redux tutorial sandbox :https://codesandbox.io/s/github/reduxjs/redux-essentials-example-app/tree/checkpoint-3-postRequests/?from-embed, but instead of connecting to a fake api, I am using firebase firestore.
Code to fetch product from firestore : ProductSlice.js
const InitialState = {
products : [],
status: 'idle',
error: null,
last: null, //to hold lastVisible when fetching data from firestore
}
export const fetchProducts = createAsyncThunk(types.RECEIVE_PRODUCTS, async (limit) => {
const resp = await fire_base_product.firestore()
.collection(collection_name).orderBy('id').limit(limit)
let result = resp.get().then((querySnapshot) => {
const lastVisible = querySnapshot.docs[querySnapshot.docs.length - 1] //how set this to redux state
const products = querySnapshot.docs.map((doc)=> {
return { ...doc.data()}
})
return {
products: products,
lastVisible: lastVisible
};
})
return result;
}
I am not quite sure on how to set this lastVisible data back into redux state, is it possible to do that with reference?
#Edit:
Tried to return both product list and last visible as an array and assign lastVisible in createSlice as stated below:
const productSlice = createSlice({
name:'products',
initialState:
reducers: {},
extraReducers:{
[fetchProducts.fulfilled]: (state, action) => {
state.products = state.products.concat(action.payload.products)
state.last = action.payload.lastVisible // this causes call stack error
}
}
});
With the above coding, two error will be reported if I run react app,
Trying to assign non serialize value into redux state
Maximum call stack size exceeded in firestore
I then tried to add middleware serializableCheck during create configuration as below:
export default configureStore({
middlleware: getDefaultMiddlleWare({
serializableCheck: {
//ignore action type
ignoredActions : ['RECEIVE_PRODUCTS/fulfilled']
// ignore path
ignoredPath: ['products.last']
}
}),
... // reducer codes
})
Even though now I have dismissed the first error, call stack exceeded still exists. Does anyone knows why this is happening ? Feel free to discuss if there is any workaround on this. Thanks.
Edit 2
Similar approach works when using context but does not work when using redux. Do I need to wrap return in promise as suggested in Firebase Unhandled error RangeError: Maximum call stack size exceeded ?
Did not managed to find a way to save lastVisible, but I found a workaround by just keeping track of the last retrieve id of my firestore data by saving it into redux state.
export const fetchProducts = createAsyncThunk(types.RECEIVE_PRODUCTS, async (limit) => {
const resp = await fire_base_product.firestore()
.collection(collection_name).orderBy('id').limit(limit)
let result = resp.get().then((querySnapshot) => {
var lastVisible = limit - 1; //only keep track of ID so we can avoid saving un-serialize coded
const products = querySnapshot.docs.map((doc)=> {
return { ...doc.data()}
})
return {
products: products,
lastVisible: lastVisible
};
})
return result;
}
And when during fetch of additional data we can then access the state by using getState() as below:
export const fetchMoreProducts = createAsyncThunk(types.LOAD_MORE_PRODUCTS, async (limit, {getState}) => {
const last = getState().products.last
var newProducts = await firebase_product.firestore()
.collection('store_products').orderBy('id')
.startAfter(last).limit(limit)
const result = newProducts.get().then((querySnapshot) => {
var lastVisible = last + limit;
const products = querySnapshot.docs.map((doc) => {
return { ...doc.data() }
})
return {
products : products,
lastVisible: lastVisible
}
})
// return retrieved data from firebase
return result
})
But doing this, I could skip the serialization check config all together as well. Not sure if this is the correct way, but this is how I got pagination working. Feel free to let me know if there is other way to approach this.
This error will come if you store non-serializable object in redux store!
if you are getting some non-serializable data from firebase, serialize it before storing it in redux store!
const nonSerializable = firestore().collection().doc(uid).get().data();
// if using this nonSerializable object in reducer, serialized it using JSON.parse(JSON.stringify())
const serializable = JSON.parse(JSON.stringify(nonSerializable))
you should save last visible path not the whole data like this
const lastVisible = querySnapshot.docs[querySnapshot.docs.length - 1].ref.path
This question already has answers here:
The useState set method is not reflecting a change immediately
(15 answers)
Closed 2 years ago.
hey all so im using react-natives-community async storage and i can't for the life of me get the state to save and im not too sure why. the first console.log from the await variable returns the correct information, but when i set the state the second console.log returns null and im not sure what's going on
const [userEmail, setUserEmail] = useState<string | null>(null);
const getEmail = await AsyncStorage.getItem('email')
console.log(getEmail + 'first')
setUserEmail(getEmail);
console.log(userEmail + 'second')
I made this custom hook but still no luck
const useGetAsyncStorage = (AsyncStorageItem: string): string => {
try {
const [data, setData] = useState<string | null>(null);
useEffect(() => {
const fetchAsyncStorage = async () => {
const AsyncStorageData = await AsyncStorage.getItem(AsyncStorageItem);
console.log(AsyncStorageData)
setData(AsyncStorageData);
};
fetchAsyncStorage();
}, [AsyncStorageItem]);
return data as string
} catch (error) {
return error
}
};
The thing is state updates are asynchronous. You will end up seeing the null for the email what it was initially . If you want to see the change , you should do console.log in useEffect() hook