React Custom Hook function keeps recalling - javascript

According to the thread below,
useCustomHook being called on every render - is something wrong with this
It says it is completely normal to keep calling the custom hook function every time React re-renders.
My questions are, if it affects on a performance side when returning an array from this Custom Hook function( Not when fetching API and receiving data ) which contains a lot of values.
If so, how to prevent it ( How to let this Custom Hook function run only once )?
Here is my Custom Hook code, it returns an array which contains around 5000 string values.
function FetchWords(url: string) {
const [data, setData] = useState<string[]>([]);
useEffect(() => {
fetch(url)
.then((words) => words.text())
.then((textedWords) => {
setData(textedWords.replace(/\r\n/g, "\n").split("\n"));
});
}, []);
const expensiveData = useMemo(() => data, [data]);
return expensiveData;
}
export default FetchWords;
My Main js
const wordLists: any[] = useFetch(
"https://raw.githubusercontent.com/charlesreid1/five-letter-words/master/sgb-words.txt"
);

CustomHooks should start with word use...
You don't need useMemo in your hook, simply return data state.
Your hook makes the fetch call only once, so no problem there as the effect has empty dependency, so it runs once after first render.
The hook stores the array of 5000 entries once in data state and returns the same reference each time your custom hook is called during component re-renders. There is no copy operation, so you don't need to worry about that.
If you only want to fetch 100 entries for example, then your backend needs to provide that api.
Hope this resolves your queries as it is not very clear what is your doubt.

If you are worried about bringing all this data at the same time, you can indicate from the backend that they send you a certain number of records and from the frontend you can manage them with the pagination.
the use of useMemo is superfluous.
the useEffect that you are using will only be rendered ONCE, that is, it will only call the 5,000 registers that you mention only once

Related

How can I start an async API fetch as early as possible in a React functional component?

The standard way to make an API call in functional React is with useEffect:
function Pizzeria() {
const [pizzas, setPizzas] = useState([])
useEffect(
() => fetchPizzas().then(setPizzas),
[]
)
return (
<div>
{pizzas.map((p, i) => <Pizza pizza={p} key={i} />)}
</div>
)
}
But, as this article points out, useEffect will not fire until after the component has rendered (the first time). Obviously in this trivial case it makes no difference, but in general, it would be better to kick off my async network call as soon as possible.
In a class component, I could theoretically use componentWillMount for this. In functional React, it seems like a useRef-based solution could work. (Allegedly, tanstack's useQuery hook, and probably other libraries, also do this.)
But componentWillMount is deprecated. Is there a reason why I should not do this? If not, what is the best way in functional React to achieve the effect of starting an async call early as possible (which subsequently sets state on the mounted component)? What are the pitfalls?
You're splitting milliseconds here, componentWillMount/render/useEffect all happen at essentially the same time, and the time spent fetching occurs after that. The difference in time from before to after rendering is tiny compared to the time waiting for the network when the request is sent. If you can do the fetch before the component renders, react-query's usePrefetch is nice for that.
Considering the scope of a single component, the earliest possible would be to just make the call in the component's function. The issue here is just that such statement would be executed during every render.
To avoid those new executions, you must keep some kind of "state" (or variable, if you will). You'll need that to mark that the call has been made and shouldn't be made again.
To keep such "state" you can use a useState or, yes, a useRef:
function Pizzeria() {
const pizzasFetchedRef = useRef(false)
const [pizzas, setPizzas] = useState([])
if (!pizzasFetchedRef.current) {
fetchPizzas().then(setPizzas);
pizzasFetchedRef.current = true;
}
Refs are preferred over state for this since you are not rendering the value of pizzasFetched.
The long story...
Yet, even if you use a ref (or state) as above, you'll probably want to use an effect anyway, just to avoid leaks during the unmounting of the component. Something like this:
function Pizzeria() {
const pizzasFetchStatusRef = useRef('pending'); // pending | requested | unmounted
const [pizzas, setPizzas] = useState([])
if (pizzasFetchStatusRef.current === 'pending') {
pizzasFetchStatusRef.current = 'requested';
fetchPizzas().then((data) => {
if (pizzasFetchStatusRef.current !== 'unmounted') {
setPizzas(data);
}
});
}
useEffect(() => {
return () => {
pizzasFetchStatusRef.current = 'unmounted';
};
}, []);
That's a lot of obvious boilerplate. If you do use such pattern, then creating a custom hook with it is the better way. But, yeah, this is natural in the current state of React hooks. See the new docs on fetching data for more info.
One final note: we don't see this issue you pose around much because that's nearly a micro-optimization. In reality, in scenarios where this kind of squeezing is needed, other techniques are used, such as SSR. And in SSR the initial list of pizzas will be sent as prop to the component anyway (and then an effect -- or other query library -- will be used to hydrate post-mount), so there will be no such hurry for that first call.

How to properly Populate a Dropdown with SQL Query Values in React.Js Hook

I have a react component called Sidebar.jsx. Within it, I am making an API call to get a array of fleets to populate an eventual JSX dropdown element within my Sidebar. This results in a simple JSON array.
I have imported a function called getFleets() from my services folder to make the API call. The service uses the fetch API to make a query call to my backend and looks like this:
export async function getFleets() {
const resp = await fetch("http://localhost:5000/fleets", {
method: 'GET',
headers: {},
mode: 'cors'
});
return resp.json();
};
However, when I use the website, it appears to infinitely make the API call. This is my first time trying to make an API call within a react component so I am a bit confused here. Other guides I've read online seem to be similar but I am obviously missing something.
What can I do to make this API call only once and retrieve my JSON array such that I can later use it to populate the options in my return ?
Sidebar.jsx
import React, { useEffect, useState } from "react";
import { getFleets } from "../services/FleetService";
const Sidebar = () => {
const [data, setData] = useState([]);
useEffect(() => {
const setFleets = async () => {
const fleets = await getFleets();
console.log(fleets);
setData(fleets);
}
setFleets();
}, [data]);
return (
<>
// Add data to <select> </select>
);
};
The way your code works, since data is part of the dependency array sent to useEffect, every time data changes the effect runs, which changes data, which runs the effect again ...resulting in the infinite loop.
The simple fix is to remove data from the dependency array, and explicitly specifying an empty array [] as the second parameter of useEffect. This will make the effect run only exactly once, when the component is first rendered.
You need to explicitly specify an empty array because when the second parameter isn't specified at all, the effect will run on every render, bringing back the infinite loop issue.

Get array of objects from real time data snapshot - Cloud Firestore

I'm trying to fetch real time data from Cloud Firestore using the below code.
export const getRealTimeData = () =>
db
.collection('posts')
.onSnapshot(
(querySnapshot) => {
const posts: any = [];
querySnapshot.forEach((doc) =>
posts.push(Object.assign({
id: doc.id
}, doc.data()))
);
},
);
};
And, I want to use the resultant array to display the data on UI. When I'm doing this, the resultant array is a function but not the actual array of data.
const posts = getRealTimeData();
Here's what I get when I log posts
function () {
i.kT(), o.al(s);
}
Could anyone please point where I went wrong?
Realtime listeners added with onSnapshot() are not compatible with returning values from function calls. That's because they continue to generate new results over time, and would never really "return" anything once. You should abandon the idea of making a synhronous getter type function in this case - they just don't work for what you're trying to do.
Ideally, you would use an architecture like Redux to manage the updates as they become available. Your realtime listener would dispatch query updates to a store, and your component would subscribe to that store that to receive those updates.
If you don't want to use Redux (which is too bad - you really should for this sort of thing), then you should wrap your query inside a useEffect hook, then have your listener set a state hook variable so your component can receive the updates.

React Hooks - useEffect, call fuctions just when I update an specific property

This is my useEffect:
useEffect(() => {
handleFetchUsers();
handleFetchBooks();
}, [month, listUsers, listBooks]);
I want to fetch books and users the first time (and also when the user change the month), and I also need fetch books and user when there are changes outside the component (when cache changes, I want to fech from cache).
The problem here is that there are 3 properties, and when books is updated, the useEffect function dispatch all fetchs, I do not want fetch users if user has not been updated.
Is there a way to fetch books just when books update and fetch uses just when users update?
Right now, I have all fetch duplicated.
I remember in componentWillUpdated, I solve this problem comparing nextProps and currentProps:
if(nextProps.users !=== props.users){
fetchUsers();
}
I want a performance like that.
You can (and should!) use useEffect more than once in a functional component. Separate your concerns this way, and you should see the behaviour you are looking for.
useEffect(() => {
handleFetchBooks()
},[month])
useEffect(() => {
handleFetchUsers()
},[YOUR_USER_DEPENDENCIES])
On every rerender react checks the dependencies array and refires the hook if any item has changed, so by keeping them separate you can control this behaviour.
useEffect was made keeping this in mind, ideally there should be a single task or lets say side effect attached.
So basically just like #Cal Irvine answer, You can have separate effects for your side effects.
useEffect(() => {
taskrelatedtomonthdependant();
}, [month]);
useEffect(() => {
taskrelatedtouserdependant();
}, [month]);

Call api before first render in functional component in React.js

If I want to call API after the first rendering of component, I know we have useEffect hook to call the API method. (I am talking about functional components only. No class component).
Is there any way, I can call the API before my component renders the first time.
The reason for this question is, If some UI part is dependent on API, I do not want to show any incomplete information to the user on the first render also, which will be changed once I get the data from API.
This seems to be a bad experience with UI.
Edit: I got a couple of advice to use useLayoutEffect or any consumable flag to check if it is rendered or not. I have checked useLayoutEffect does not work, and by using the consumable flag, we are increasing the complexity only.
Do we have any better way for this?
I think useLayoutEffect can be used for something like this, by passing in an empty array as second argument. useLayoutEffect(() => {...}, []);
Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint.
Although you can always fetch the data in the parent component and pass it as props. Or - if you don't mind it being an experimental feature for now - React Suspense is trying to solve this exact problem.
There are no correct ways to make API call before component rendered from the same component.
You may preferred make API call in parent component and render presentation component when and only when have consumable data.
Another workaround for such case is keep consumable flag inside component, make request inside useEffect, render nothing or some kind loader and render something only when request completed with success.
on calling api it is not responding exact on its first render but giving exact response when it's being hit second time
You can have a spinner or loading component be rendered first conditionally (isLoading for example):
if(isLoading) return <Spinner />
and have the api call set (isLoading) to false on status 200 for example.
Just came across something, which may help someone in future. So we can use some library but the specific one I would mention here is React Query
React query does exactly what we are trying to achieve, the hooks like useQuery fetch data as soon as rendering starts so you don’t have to wait until react loads the entire component as follows
// with react query
const { status, data, error, isFetching } = useQuery(
['data'],
async () => {
const data = await (
await fetch(`${API_BASE_URL}/data`)
).json()
return data
}
)
// without react query
useEffect(() => {
try {
setLoading(true)(async () => {
const data = await (await fetch(`${API_BASE_URL}/data`)).json();
setData(data);
})();
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
}, []);
Here is the article link if you want to read

Categories

Resources