We have a React Redux app which requires long running ajax to show its main content.
Consider for Example showing list of some items, and fetching that list requires 1-2 seconds.
Currently i have put the ajax call in the componentDidMount of the listing component. but i can see the call takes around 500-900 millis before starting. This adds up to Backend latency not creating very favourable experience.
We want the Ajax call to be triggered as early as possible. I have thought of triggering my ajax in index.js or public index.html and storing the result in window variable later in componentDidMount i will read it, But my solution sounds more like a hack to me can anyone recommend anything better?
You can do the call in the constructor of the component. There is also a lifecycle method called componentWillMount that you can use. Both these methods will start the ajax call before the component is mounted. It might be worth it if it is a large component that you mount.
On the other hand, if your delay comes simply from downloading and parsing the javascript code, it is harder to optimize. Then I would recommend checking out server-side rendering or code splitting. But those solutions will take quite some time to implement.
Related
The official React docs says that you should fetch data from server in componentDidMount lifecycle method.
It could sound a bit weird that in case when your component need some data (which is pretty often) you should always wait for initial rendering, including slow DOM api manipulations for the component and its children. It provides unreasonable delay in sending the request to server.
How could you explain why it is a good decision for React team not to implement a special lyfecycle method like componentFetchInitialData (which is invoked at the beginning) for initial data fetching?
The question from the interview that made me confused
Let's add a <FlatList/> into our application.
The first requirement we have is to render a predefined set of 5 items. We define a constant in our component, pass it into the list via the data prop and it works just fine...
... until we decide to store this data on a server and expose it via the API. OK, no problem, will fetch the data in our componentDidMount() method, put it into the state when it finishes loading, pass the state to the data prop and it also works just fine...
... until we notice that we have a huge delay before we can show the first item of the list. That is because the amount of data we're loading from the API grew significantly over time. Maybe now it is some REST resource collection consisting of thousands of items.
Naturally, we decide to implement a pagination in our API. And that is when the things start to get interesting... When do we load the next page of the resource collection? We reach out to the wonderful React Native API reference, examine the FlatList part of it, and figure out that it has a very handy onEndReached callback prop. Wonderful! Let's load the next page of our collection every time this callback is called! It would work as a charm...
... until you receive a bug report in your mail. In this report a user tells us that the data is not sorted properly in the list, that some items are duplicated and some items are just missing.
After a quick debugging we are able to reproduce the issue and figure out what causes it. Just set the onEndReachedThreshold = { 5 } and scroll the list very fast. onEndReached callback would fire asynchronously before the previous one has finished.
Inside our component, we have a variable pageId storing the last page ID we loaded. Each time the onEndReachedThreshold gets fired we use it to construct the next page URL and then increment it. The problem is that this method is called concurrently and the same value of pageId is used multiple times.
I used to do a bit of multithreading programming before, I've heard of mutexes, semaphores, and atomicity. I would like to be able to acquire an exclusive lock on the pageId to use it in this concurrent callback.
But after a quick Internet search, it seems that JS does not provide such tools out of the box. I found some libraries like this one but it doesn't look like a good candidate for a dependency, it's not very actively developed, it's not made by a major vendor etc. Looks more like some hobby project.
The question is: what are the industry-standard rock-solid tools or patterns for thread-safe React Native programming? How can I solve the described concurrency issue in a React Native application?
I am reading up on react lifecycle and am getting a little confused. Some recommend using componentWillMount to make ajax calls:
https://hashnode.com/post/why-is-it-a-bad-idea-to-call-setstate-immediately-after-componentdidmount-in-react-cim5vz8kn01flek53aqa22mby
Calling setState in componentDidMount will trigger another render()
call and it can lead to layout thrashing.
and in other places it says not to put ajax calls in the componentWillMount:
https://medium.com/#baphemot/understanding-reactjs-component-life-cycle-823a640b3e8d
...this function might end up being called multiple times before the
initial render is called so might result in triggering multiple
side-effects. Due to this fact it is not recommended to use this
function for any side-effect causing operations.
Which is correct?
The React docs recommend on using componentDidMount for making network Requests
componentDidMount() is invoked immediately after a component is
mounted. 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.
Calling setState() in this method will trigger an extra rendering, but
it is guaranteed to flush during the same tick. This guarantees that
even though the render() will be called twice in this case, the user
won’t see the intermediate state.
As per the case for componentWillMount:
EDIT:
This lifecycle is deprecated since v16.3.0 of react and is no longer encouraged for usage.However its renamed to UNSAFE_componentWillUpdate and is expected to work till at least v17 of react
Before v16.3.0
An asynchronous call to fetch data will not return before the render happens. This means the component will render with empty data at least once.
There is no way to “pause” rendering to wait for data to arrive. You cannot return a promise from componentWillMount or wrangle in a setTimeout somehow. The right way to handle this is to setup the component’s initial state so that it’s valid for rendering.
To Sum it up
In practice, componentDidMount is the best place to put calls to fetch data, for two reasons:
Using DidMount makes it clear that data won’t be loaded until after
the initial render. This reminds you to set up initial state
properly, so you don’t end up with undefined state that causes
errors.
If you ever need to render your app on the server, componentWillMount will actually be
called twice – once on the server, and again on the client – which is
probably not what you want. Putting the data loading code in
componentDidMount will ensure that data is only fetched from the
client.
componentDidMount is the recommended lifecycle method to make Ajax calls as described in their docs
ComponentDidMount is the place.
But if you have time try to look at Redux and make the requests in actions, as your application grow it will help a lot to manage the app state.
;)
Here's my problem:
I'm new at reactjs and I'm trying to make this app using SWAPI (swapi.co). For now I need to list characters and some info about them. The problem is I have this component called SelectedCharacter that returns some info about a character that was selected in a div.
The moment a character is passed through props to this component, I get a response via xmlhttp and the info is displayed. The thing is that I want to put a "Loading..." message while the data is fetched. This is how I was trying to figure it out:
I set up the componentWillReceiveProps function, where I test if I'll need to load stuff and the componentDidUpdate, where I fetch the data from this api and update the status.
I know, from react life cycle, that a render is called between componentWillReceiveProps and componentDidUpdate, and it indeed is.
I expected, then, that if I did this:
render() {
if (criteria) {
return <div>Loading...</div>
}
}
The thing is: even if this criteria is true (I tested it using console.log()), the message doesn't show until the next re-render. Am I doing anything too wrong here? If it helps, my code is at github.com/piubellofelipe/StarWars, the problem is at the selected_characters.js, in the src paste.
Thanks
I've been looking at your code, trying to work this out for you and I don't have any concrete answers for you, but I've noticed a few things that may be making things a bit unpredictable.
1. Calling forceUpdate
componentWillReceiveProps(){
this.setState({loading:true})
this.forceUpdate();
}
Calling setState will trigger a render, so the call the forceUpdate is not required here. This means there are more renders occurring than you might expect.
I believe this may be the cause of your issue for a pretty complicated reason. From the setState docs
... setState() is also asynchronous, and multiple calls during the same cycle may be batched together.
And from the forceUpdate docs
Calling forceUpdate() will cause render() to be called on the component...
My theory is that the call render triggered by setState, asynchronously setting loading to true, is being delayed and the one from forceUpdate is sneaking in first, while loading is still false.
2. Updating props
this.props.selected.moviesList = moviesListNames;
Components should never, ever, update their own props. Usually, this would be stored in state instead.
For more details on this, read this section of the docs and this answer.
3. Importing axios, but not using it
import axios from 'axios'
This one isn't really an issue (other than an unused import), and might just be preparation for where you're heading. axios gives a much nicer developer experience than XMLHttpRequest (in my opinion) for http requests and will clean up the fetchData function a lot, which will make troubleshooting easier.
I hope this helps a bit, and you're enjoying React. Feel free to follow up on any of these points in the comments or as new questions.
I'm writing a react/redux app that has a Job object that fires Events from time to time. On these Events the user is expected to react. I can easily write the React component that shows such a job or events, but don't know where to put the logic when
the user is not on the Job page
and an Event happens
and I would like to pop up a notification
Of course, once I get access to the store, I can create the notification too.
Shall I handle such logic in a React container that render() nothing?
Or handle it with vanilla JS when I load a Job data?
Or to handle it in the reducer when the data is stored? (probably not recommended)
My problem with the option two is that I don't know how to access the redux store from a totally ad-hoc code, as my store is not a global object.
What do you recommend?
Generally speaking, the two places that you'd put code that needs to access the store (for either dispatching or receiving state updates) is in middleware or a connected component. If you specifically want to respond to an action coming through, then you would need to use middleware.
I actually threw together a sample timer middleware in a chat discussion a few weeks ago. I just pasted it as a gist here as an example: https://gist.github.com/markerikson/ca96a82d6fdb29388aca4052a9455431 . Illustrates the idea of doing something asynchronously in the background.
You may also want to look through the list of middlewares in my Redux addons catalog: https://github.com/markerikson/redux-ecosystem-links/blob/master/middleware.md . Numerous examples of ways to execute background-ish logic.
Finally, https://github.com/yelouafi/redux-saga (which is itself a middleware) is a fairly common lib used to do background-ish logic.