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
Related
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.
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
I have a functional component. Basically, the page consists of a form - where I need to populate some existing data into the form and let the user update it.
I'm using a hook that I wrote to handle the forms. What it does is this
const [about, aboutInput] = useInput({
type: 'textarea',
name: 'About you',
placeholder: 'A few words about you',
initialValue: data && data.currentUser.about,
})
about is the value and aboutInput is the actual input element itself. I can pass an initialValue to it too.
At the beginning of the component I'm fetching the data like so:
const { data, loading, error } = useQuery(GET_CURRENT_USER_QUERY)
This only is executed on the client side and on the server side data is undefined.
Hence this code only works when I navigate to the page through a Link component from another client-side page.
It doesn't work for:
When I go the URL directly
When I navigate to this page from another SSR page(which uses getInitailProps)
I don't want to use lifecycle methods/class component(since I'm using hooks, and want to keep using the functional component.
Is there a nice way to achieve this in Next JS and keep using functional component?
You can fetch client-side only data using the useEffect Hook.
Import it first from react
import { useEffect } from 'react';
Usage in the component looks like follows
useEffect(() => {
return () => {
// clean-up functions
}
}, []);
The first argument Is a function and you can make your API calls inside this.
The second argument to the useEffect will determine when the useEffect should be triggered. If you pass an empty array [ ], then the useEffect will only be fired when the component Mounts. If you want the useEffect to fire if any props change then pass such props as a dependency to the array.
If you want GET_CURRENT_USER_QUERY from the query string you can pass the argument from the getInitailProps and read this as props in the useEffect array dependency.
I see you mentioned getInitailProps. So you probably know what it does and its caveats. If you want the data to be defined on server side, why not use getInitailProps on the page which the form is in. That way you can retrieve the data from props and pass it to your form component. It should work either directly visiting the url or visiting from your other pages.
When Promise.all resolves and the new activity is saved, the user should be routed to /activities to view their newly created activity. Everything works as expected, however I currently need to refresh /activities page (once) after being routed in order to view the new activity in the table.
const handleSaveActivity = e => {
e.preventDefault();
Promise.all([
addActivity(),
saveActivity()
]).then(() => {
props.history.push('/activities');
})
};
I'm not sure how to re-render the page automatically after pushing a new history state, so the user does not need to manually refresh the page to see the new state. Happy to provide more code snippets if I left out something critical.
Hi i must be a little late to answer this, but this issue can be due to the wrong use of useEffect, if you have lets say a todo list and you wanna fetch data with axios for example, it would look like this:
useEffect(()=>{
axios.get(`${YOUR_URL}/todos`)
.then((res)=>{
setTodos(todos=res.data)
})
},[])
now as you can see we have initial value of an empty array, so this is acting as a ComponentDidMount, what you might want is to re render the component after it gets a new value, so you want to have a ComponentDidUpdate effect, so you would just not initialize the value as an empty array, therefore it would look like this:
useEffect(()=>{
axios.get(`${YOUR_URL}/todos`)
.then((res)=>{
setTodos(todos=res.data)
})
})
Hope this helps someone, couse i landed here due to the same issue and came to solve it this way.
just to run this.setState({whateverKey:whateverValue})?
In your activities page (call it Activities component) you should call API to get the updated data every time browser hit this component URL.
With class based style, you should do it in componentDidMount life cycle hook
class Activities extends Component {
// ...
componentDidMount() { loadActivities() }
// ...
}
With function based style, you should do it in useEffect hook
import React, { useEffect } from 'react'
const Activities = () => {
useEffect(() => { loadActivities() });
}
https://github.com/supasate/connected-react-router Please use this package, it solves the problem.
This issue I've faced a few minutes ago...however I finally found the solution by manually using the vanilla javascript. => for refreshing the page you can use
=> window.location.reload(false); after using the push property.
I'm confused, and I have searched a lot for the answer to this (seemingly) basic question.
I'm learning React, and I have a rather common component hierarchy with one top component (lets call it App) which contains a number of subcomponents (a grid, a graph, a table etc).
They all show information regarding one product.
Now when I select a row in the grid, I want to inform the other subcomponents about the change. App therefore passes a callback method
onSelectedProduct={this.onSelectedProduct}
to the grid. This gets called OK. In this App method I set the state:
onSelectedProduct(product) {
this.setState({ product: product });
}
In its render(), App has declared another subcomponent:
<ProductGraph product={this.state.product} />
Since ProductGraph needs to fetch some data asynchronously "to-be-rendered" later, where should I catch this property change??
The old "componentWillReceiveProps" sounded like the proper place, but will be deprecated and should not be used, I understand.
I have also tried shouldComponentUpdate, getDerivedStateFromProps and even to catch it in render, but they all have downsides and eventually lead to horrible code.
Somewhere, somehow, I should be able to detect that props.product !== state.product and issue an async load call for the data...
When the async method I call returns with the data, it will set the state and render itself.
So where is the optimal place to catch changed properties?
I have read a lot about the React Lifecycle but I just can't seem to find this basic information. Am I stupid or maybe blind? Or have I got this completely wrong somehow?
You are looking for componentDidUpdate() the lifecycle method that triggers when a component receives new props or has an updated state.
https://reactjs.org/docs/react-component.html#componentdidupdate
In your ProductGraph component, you would do something like:
componentDidUpdate(prevProps){
if(this.props.product !== prevProps.product){
fetch(`myApi/${this.props.product}`)
.then(res => res.json())
.then(data => this.setState({ data: data })) <-- identify what you need from object
.catch((errors) => {
console.log(errors)
})
}
}