I've been using hooks in my React project for a while now and have developed a hook for getting data, similar to many others, which I use like this.
const [callApi, isLoading, result, errrors] = useData();
callApi is a function which you call with your data and axios config, it makes the API call and updates the other variables. I deal with the result in a useEffect() call which sets the state when it receives new data. It's fairly simple.
The problem is that while it works very well for one request at a time, it has a single isLoading state and a single result state, which is no good if it's called before the previous call is finished. Let's say my user sending a message to his friends and the UI shows a list of friends with a "send message" button next to each name, they might click 3 buttons and make 3 calls in quick succession. We then have a race condition to see which one sets the result first.
I'm used to imperative programming with promises, in my old Angular code the button click would call an event handler which would call the api and the promise .then() would then deal with the result. This could happen 100 times simultaneously and each one would resolve seperately and then update the UI.
So my question is, is there an idiomatic way of doing this declaratively using react hooks so that I can fire off multiple requests and deal with the loading/result/errors separately?
Related
I found some old post that a guy having problem with his code in React and alot answer said that it was caused by because useState is asycnhronous. I was curious what it mean by asycnhronous and I found this article
useState is an asynchronous hook and it doesn't change the state
immediately, it has to wait for the component to re-render. useRef is
a synchronous hook that updates the state immediately and persists its
value through the component's lifecycle, but it doesn't trigger a
re-render
But i still don't understand what it means and why is such a problem. Can someone give me an example?
I would say this is one of the myth to call useState asynchronous.
IMHO, if you open the source code of react, I'm speaking to react 16.x and below (because i didn't read after 16), useState isn't asynchronous.
So why are we saying useState behaves as asynchronous code? We need some coding example.
const App = () => {
const [value, setValue] = useState(1)
const onClick = () => {
setValue(2)
console.log(value)
}
return <input value={value} />
}
In the above typical setup of useState, when you call setValue(2), value wouldn't become 2 right away. This is apparent if you put a console.log after that. Then why the input does show 2 instead of 1, you might ask?
That's because React made a second render to App, you can think of App has been called one more time after setValue. For this reason, people call useState async.
But then why I'm not calling useState async? well, this is a bit long story. The short story is that useState is implemented inside React using plain non-async codes, therefore you can't say a code async just because it has been called twice or it has been deferred to call. Async means a bit differently in Javascript. This is another topic which I won't get into here.
I don't want to speak for the React team as to why state changes are asynchronous, but here's some random dude's explanation. I'll be referring to the JavaScript event loop, which is basically a program that runs many times per second. Each time it runs, it processes a series of tasks synchronously. Asynchronous code is executed across several event loops. This is a very basic and oversimplified explanation - you should go read more about the event loop.
So your initial state declaration happens in your initial render - the first time the function is called. This initial render happens synchronously - meaning the function is executed top to bottom in a single event loop. It's worth pointing out that React components cannot be async:
const SomeComponent = () => {
const [foo, setFoo] = useState("bar");
return <div>The value of foo is "{bar}"</div>;
}
After your initial render, the state only changes when something happens: user clicks a button, the screen resizes, a timer interval fires, etc. Whenever an "event" happens, there can be a lot of processing going on in the JavaScript event loop.
Let's look at a button click, the event propagates all the way down and back up the entire DOM tree which contains the button. The browser is also rendering different states of the button, causing the browser to repaint. You also have developer code which is running in the button click handler which might be doing some heavy lifting like iterating over an array. This all happens within a single event loop. The point of this explanation is to convey that "a lot is going on". Eventually, the state will get updated with setFoo(...).
At this point, React now has to figure out which component changed, calculate and rerender your application in a virtual DOM, perform a diff with the actual DOM, and then apply any changes to the actual DOM. Only at this point does the browser finally paint the result to the screen. This whole operation can be very taxing on a CPU.
If react were to handle state changes synchronously, all of this virtual DOM diffing and calculating would happen in the same event loop as the initial button click. It is very likely that most apps would cause the browser to sporadically freeze and jitter because the CPU is doing so much work at one time.
There are also some other things to consider. Suppose the window resizes and a bunch of different components perform some calculations and update their state. You don't want react to synchronously rerender every time a component changes its state. You want react to kind of wait and collect a whole bunch of state changes and process them at the same time. Browsers also provide mechanisms like requestAnimationFrame to help web-based applications asynchronously render in such a way that works best for the user and their system/browser resources.
There are many other things to consider, but this post is getting long. Hope this helps.
I'm creating a website which does a lot of api calls, it is a really big project, when it grew up, i started having some problems with backend's data, some components was depending on another components to receive data, and i had to create many if to check if the data already was loaded to show them. With this situation, i've had some doubts about good practices
Should i load every data from backend in the very beggining? for
example in app.js file and then pass the data already loaded
through props?
Is it a good practice calling api in useEffect? cause it may delay
some seconds, it won't be nice to user
If you have loading states there shouldn't be an issue with fetching data for multiple components progressively throughout your app. Typically I would use a container component where the API call would be made. Then that renders conditionally the loading state or the "dumber" child component that holds and renders the data. Getting data when you need it is probably better than getting all of it and having it when you don't.
This first question feels very opinionated and kinda feels like a case by case basis decision. I want to clarify as well that this is indeed my opinion on this question.
Calling API inside of a useEffect is an entirely legitimate use case. For example say you have an infinite scroller that renders on your page. You could have a useEffect make the initial call to the API to get the first set of results. Regardless, yes this is fine.
useEffect(() => {
someAPICallForInitialData();
}, []);
...some code here
<InfiniteScoller onScroll={() => getMoreData()}>
{chidren}
<InfiniteScoller />
I have a code in which an API call is made inside the render method in a react app, is it correct?
The API call is made from a function placed inside a const function which is placed in another file
render method is place for presenting various elements to user and these elements can be used to trigger an api-call for various purposes. For example - checking validity of text entered in input-element, sending form-data async-ly to server, etc.
And this is a nice practice to put the code into various modules or separate files for better management, etc. for ex. call is wrapped inside a function and placed in a different file. Webpack helps in filling all various modules in place.
So it is alright to put api calls in render and calling a function defined inside another file for the same.
It is not correct.
1) If the api call is waiting for the call to complete(await or promise resolve), Then it is essentially blocking render.
2) React might call render function many times[it may or not lead to DOM update], which means you will be making redundant api calls as many times.
It is ok to do so, if it is in response to an event.
So No, just put the api call in componentDidMount or ComponentDidUpdate or if you are using hook, add a useEffect.
Good Luck!
It really depends on the use case.
There is nothing wrong with doing so as long as you aren't calling it everytime it re-renders. I assume you are making the request upon triggering of certain events(such as onClick). If yes, it may be cleaner to encapsulate it within a method.
async doSomething = () => {
// do the rest
// make API request
await getRequest();
}
return <>
<button onClick={() => this.doSomething()}/>
</>;
If the request is to be made only once and when the component is initialised, you should make that request within the componentDidMount lifecycle hook. According to the documentation for componentDidMount,
componentDidMount() is invoked immediately after a component is
mounted (inserted into the tree). Initialization that requires DOM
nodes should go here. If you need to load data from a remote endpoint,
this is a good place to instantiate the network request.
Otherwise, calling it on render() will result in the API request to be triggered everytime there is a re-render.
I'm in trouble with React and Flux... We have an application that is pretty similar to the new Flux chat example. We have the famous error "cannot dispatch in the middle of dispatch". But, it's hard to us to think in a good way to resolve this problem in some cases.
Our doubt is identical to this: https://groups.google.com/forum/#!topic/reactjs/mVbO3H1rICw, but I can't understand very well the solution adopted. As far as I understand, is not a very elegant solution.
Here is the sequence of events:
Action A is dispatched;
The Store updates it's internal state and emits the change message;
A react component X receives the change message (by the callback of the listener) and updates it's state (setState);
The component X renders and as part of that a new component Y is mounted too. We choose the component (Y, Z, etc...) to be rendered using the information of the state;
The new component Y needs data to display that isn't initially loaded. So we call a API in the componentDidMount() of the component Y, that calls an action B.
Then, with the new dispatcher in the Action B, we have this dispatch error.
If you consider that our application logic have some issue, I can bring some practicals examples to show why this scenario is common for us. Any idea of how refactor this "flux" is very welcome.
Thanks for any help!
i think you need to use the waitFor token from the dispatcher before launching action b (youre using the dispatcher from the flux npm module right?). additionally, what can you do is make the ajax call from your store during action A if youre always going to need that data from action b.
solution b would look like this in /* store.js */
Dispatcher.register(function(payload){
switch payload.type {
case ACTION_A:
/* do your state logic */
StoreDataAccessLayer.apiCall()
}
}
where you have a Data Access Layer class / object that wraps your ajax call to the api. using the state from your store as inputs, and calling trigger change from the success function. this is the primitive solution i've seen when using the flux npm module if youre not going with waitFor
I have a Calendar component which, when rendered with a certain prop called LoadOnMount needs to call the server to load a set of flights.
The problem is that I update the calendar by listening to the CalendarStore's Events.UPDATE event, which is triggered by the UPDATE_CALENDAR action dispatched by my Dispatcher, and I load the set of flights using another action called LOAD_FLIGHT_LIST. So when I invoke this new action from the Calendar component's ComponentDidMount function I get the cannot dispatch in the middle of a dispatch error.
Any way to handle these sorts of dependencies? (The calendar is also fetched from the server)
You have two issues that I can identify:
The first is that you are trying to get the dispatcher to dispatch during a dispatch. That's not the way you should be doing it.
The second is that you seem to be performing AJAX/async calls from inside your dispatch handler. I don't want to say that you should never do that, but that does not seem to be necessary in your application.
Here's a link to another stack overflow question that I think is similar: https://stackoverflow.com/a/23637463/2152216
The difference is that the asker is trying to perform an Ajax call from within his dispatch handler, while you seem to be trying to dispatch an event that will in turn trigger an ajax call during the event's handling.
What you can do is create an action that asynchronously loads the flight list, then dispatch the FLIGHT_LIST_LOADED action afterwards passing it the fetched flight list. Your store should handle this action and broadcast a change event for all the component observers.
I hope you understand what I'm talking about. If you think I misunderstood your problem, let me know.