Why is my state not properly rendered in reactjs? - reactjs

In my project I use ReactJS in combination with redux and firebase.
Creating a thunk to make async calls to firebase and store the data in redux.
When I'm fetching my files from firebase storage.
Using this method:
try {
let list = [];
await storage
.ref()
.child(path)
.listAll()
.then((res) => {
res.items.forEach((item) => {
storage
.ref()
.child(item.fullPath)
.getDownloadURL()
.then((urlRes) => {
list.push({
name: item.name,
url: urlRes,
});
});
});
});
dispatch(getFileActionSuccess(list));
This method works as intended.
It returns an array of files with their url to view/download them.
The problem is when I try to access this object in my state, it returns an empty array.
Even though when checking using Redux Devtools, I can clearly see that after the list was dispatched. And I could see the correct data.
Devtools image
Note: this is not the real code but a representation
function page() {
getFiles();
<filesList/>
}
function filesList() {
const files = useSelector((state) => state.files, _.isEqual);
console.log(files);
return (..insert render..);
}
But when logging the files. It shows an empty array at first. But when expanding it, it shows the correct data. But it doesn't render it. As I don't understand why it isn't showing like it is supposed to I no longer know what to do and how to fix this.

Simply fetch the data on component mount and component update, and update your state accordingly.
If you’re using React Hooks, you can use React.useState() and give it a dependency. In this case the dependency would be the part of your state which will update upon completion of your HTTP request.

Related

Firebase firestore read operation very high

So basically im making a CRUD app using react and firebase firestore for the backend.
My write and delete operation is doing well, there is no problem with it.
But my read operation have problem.
My web is getting all document from a collection in firebase using useEffect. So this only run whenever it first mount (when my web load first time) and when im changing "users" value when doing delete and create operation
this my code:
useEffect(() => {
const getUsers = async () => {
const querySnapshot = await getDocs(collection(db, "cobadata"));
setUsers(querySnapshot.docs.map((doc)=> ({...doc.data(), id: doc.id})))
};
getUsers();
}, [users]);
idk whats wrong but im getting a very high read operation when im test the web, its like every one read operation i do in my website, its getting like hundred operation in the firebase. i can see this in my firebase console, when im using the web just like 5 minute in my firebase console the read operation reaching 20k< operation.
can anyone help me how to deal with this, thanks!
You dont show all of your code here, so I will need to do some guessing.
Your useEffect has a dependency array that now is set to [users]. This means that every time the variable users changes your useEffect will rerender. Inside your useEffect you then set a new value to users by the setUsers function. Even if you get the same values returned from firebase regarding the current users, you still create a new array each time you read data. (querySnapshot.docs.map((doc)=> ({...doc.data(), id: doc.id}))). React only does a shallow comparison, meaning that the object reference has changed, and therefore users is different on each render.
First you need to decide when you want to run the useEffect and what should trigger it. If changes in the variable users is not the correct place to check, then I would remove users from the dependency array.
One solution could be to move the functionality in your effect into its own function and wrap it in an useCallbac. You can then call this function from an ´useEffect` on initial load, and after that simply load the effect whenever you delete or create users. Something like this.
const getUsers = useCallback(async () => {
const querySnapshot = await getDocs(collection(db, "cobadata"));
setUsers(querySnapshot.docs.map((doc)=> ({...doc.data(), id: doc.id})))
}, [collection])
useEffect(() => {
getUsers()
}, [getUsers]);
const createUser = () => {
...
getUsers()
}
const deleteUser = () => {
...
getUsers()
}
(PS! I would recommend adding the eslint-plugin-react-hooks to your eslint-config. This will give you some warning if your hooks are used wrong)

How to use zustand to store the result of a query

I want to put the authenticated user in a zustand store. I get the authenticated user using react-query and that causes some problems. I'm not sure why I'm doing this. I want everything related to authentication can be accessed in a hook, so I thought zustand was a good choice.
This is the hook that fetches auth user:
const getAuthUser = async () => {
const { data } = await axios.get<AuthUserResponse>(`/auth/me`, {
withCredentials: true,
});
return data.user;
};
export const useAuthUserQuery = () => {
return useQuery("auth-user", getAuthUser);
};
And I want to put auth user in this store:
export const useAuthStore = create(() => ({
authUser: useAuthUserQuery(),
}));
This is the error that I get:
Error: Invalid hook call. Hooks can only be called inside of the body
of a function component. This could happen for one of the following
reasons.
you can read about it in the react documentation:
https://reactjs.org/warnings/invalid-hook-call-warning.html
(I changed the name of some functions in this post for the sake of understandability. useMeQuery = useAuthUserQuery)
I understand the error but I don't know how to fix it.
The misunderstanding here is that you don’t need to put data from react query into any other state management solution. React query is in itself a global state manager. You can just do:
const { data } = useAuthUserQuery()
in every component that needs the data. React query will automatically try to keep your data updated with background refetches. If you don’t need that for your resource, consider setting a staleTime.
—-
That being said, if you really want to put data from react-query into zustand, create a setter in zustand and call it in the onSuccess callback of the query:
useQuery(key, queryFn, { onSuccess: data => setToZustand(data) })

React multiple http calls when navigating the application using the URL

I have a component which has filtering, searching and pagination capabilities. What I'm trying is to create a queryString and attach to the URL such that I can later copy and paste it in another browser so that I can reuse the filters.
To extract the query params from the URL I'm using the useLocation hook
const useQuery = () => new URLSearchParams(useLocation().search);
const pageNo = useQuery().get('page') ?? 1;
I'm using the useEffect hook to track for changes of the page query parameter value, and dispatch an action which will update the pageNo in the state object of my reducer.
React.useEffect(() => {
dispatch({
type: actionDescriptor.CHANGE_PAGE,
payload: pageNo
});
}, [pageNo]);
I have another useEffect hook which handles the fetch of the data, and gets triggered when the pageNo changes. I'm using the useNavigate to create and navigate to the new location if the http call was successful
const nav = useNavigate();
React.useEffect(() => {
(async function get() {
const response = // make http call and get response
if (response.status === 200) {
dispatch({
type: actionDescriptor.FETCH_SUCCESS,
payload: {
data: response.data['value'],
}
});
nav (`/data?page=${state.pageNo}`);
}
/// handle error
}
})();
}, [state.pageNo, state.pageSize, state.filter]);
When I'm navigating using the UI, selecting a different page for example, all works well, there is a single http call, the browser url is updated as expected (e.g. localhost/mydata?page=2). If however I'm copying the url and paste it in another window, it makes two http calls, and renders the dom twice. What might be the cause for this?
my guess is due to the parameters you are listening on state.pageNo, state.pageSize, state.filter. I'm assuming all of these are null/empty at the beginning of your app. Once you copied and pasted the url, two of these parameters will change which will cause the useEffect to be called twice.
put in a console.log in the useEffect to confirm that. Once that's confirmed, I would re-examine the list of parameters to see if you need to listen to all of them.
I would take a look at the pageNo. It looks like it might be changing from default value to 2 since you have 2 useEffects probably firing for the same render.

how to save the data from firebase I recently Created with axios and react?

I am kinda new into react and web dev overall, I want to ask, where is the issue in my proble?
I created a database with firebase, posting into it went fine, but now I am trying to GET the date I posted before and store it Into a variable, so then I can iterate through the data and map different components for each data. I am using axios, here is the code:
function CreateBlog(props) {
const [fetchedData, setFetchedData] = useState();
useEffect(() => {
axios.get("https://diary-page-default-rtdb.firebaseio.com/diaryPages.json")
.then((response) => {
console.log(response.data);
setFetchedData(response.data);
console.log(fetchedData)
})
.catch(error => console.log("error occured:", error))
}, []);
so as I console.log the response.data I get the object with the data stored in the database, but when I setFetchData and console.log the fechedData I get undefined. Is there any simple way to store the data inside "fetchedData" as an array where every different object represents a part of the array so that later on I can map through the array and display the data in separate components?
You are storing the data correctly, but you are not able to console.log them straight away because the useState operates asynchronously.
If you want to console.log your fetchedData, have a useEffect listening to changes on that state (for demonstration purposes):
useEffect(() => {
console.log(fetchedData)
}, [fetchedData]);
A further suggestion I would give (not essential though), is to set your initial state as an empty array since that's the type of data you are storing:
const [fetchedData, setFetchedData] = useState([]);
From here, you can map through your data as such:
{fetchedData?.map((data, index) => <div key={index}>{data}</div>}
Just make sure data is not an object if you copy my example, or it will return you an error since you can't display objects inside jsx

Reload redux on save

I am working on a React.JS project, based on the react-boilerplate templates. Unfortunately, react-boilerplate only has an example of loading remote data into redux. There is no save example.
I was able to write the save actions, reducer and saga, no problem. It is all pretty standard stuff. However, one issue holding me back, which I was unable to resolve - reloading the store after saving.
I did the below:
const mapDispatchToProps = dispatch => {
return {
loadEvent: eventId => dispatch(loadEvent(eventId)),
saveEvent: values => {
const event = dispatch(saveEvent(values))
return dispatch(loadEvent(event.id || values.id))
}
}
}
I want the above code to work as a promise - reload the event by id after save finished to completion.
It is not working like I need it to. I get load invoked, yet there is no new data in the store.
You should create some xxxx_REQUEST and xxxx_SUCCESS|FAILURE action types to each request (not important it is saving or not).
I don't know you are redux-saga or redux-thunk but after your request fetch finished, you should dispatch xxxx_SUCCESS|FAILURE then in your reducer, get data and store it on you store.
Then you could use a selector to get data from redux store in your component.
I resolved this issue by sticking everything inside my saga as below:
try {
// Call our request helper (see 'utils/request')
const createdEvent = yield call(request, requestURL, {
method: !isNaN(id) && id !== undefined && id !== null ? 'PUT' : 'POST',
body: JSON.stringify(event)
})
yield put(eventLoaded(createdEvent, id))
yield put(loadEvent(createdEvent['id']))
} catch (err) {
yield put(eventLoadingError(err))
}
Now, the thing works as I need it to.

Resources