I am learning how to use Reactjs and I read the following post :
https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#fetching-external-data
When componentWillMount is used, it is written that The above code is problematic for both server rendering (where the external data won’t be used) and the upcoming async rendering mode (where the request might be initiated multiple times).
I do not understand :
How the request might be initiated multiple times because
componentWillMount is used only one time.
Why componentDidMount solves this problems. For the server rendering,
the external data won't be used also in the first render call.
According to the React docs, changing your component’s state in componentWillMount will not trigger a re-render. This means that if you make your AJAX call and set the response in your component state, it will not re-render, which means you won’t see your data in the DOM. (Remember that the component was initially created with an initial state which most likely didn’t have the data from your external data/AJAX call response)
You could argue that wouldn’t it be better to do my AJAX call to pull external data before the component mounts for the first time?
It wont be better because you don’t know how much time it will take to do your AJAX call. Your AJAX request could take longer to get the data than the time it takes the component to mount and therefore your data does not show up on your DOM as the component has already rendered and there is no re-rendering happening. Your AJAX request could take longer for any reason - your user is on mobile and has slow internet, some issue with your server is making it slow to return responses, etc...
Best thing to do is make your AJAX call in componentDidMount and make your component handle empty data (probably display a loading spinner) until your AJAX request returns the data, sets it to the component state and triggers a re-render! :)
If you read further down they explain a bit more why componentWillMount is problematic.
The above code is problematic for both server rendering (where the
external data won’t be used) and the upcoming async rendering mode
(where the request might be initiated multiple times).
But these may be rendered moot as react is essentially deprecating that lifecycle function come react 17, and thus currently is renamed to UNSAFE_componentWillMount and not recommended for use, but instead use componentDidMount to make your async data fetches.
Why does componentDidMount fix this?
Because the server is pre-rendering the components/JSX, but you don't want the component to fetch its data until after it is actually mounted and running in a browser.
react component lifecycle docs
Related
I currently have a React app that I'd like to use SSR with. All but one component is pretty much static content, making SSR super easy. Everything but the component is rendered just fine and well right now.
My question is how do I go about rendering this component that needs to first get data? Because it's a complex SVG that gets rendered my line of thinking is that having it "update" once data comes in is a bad move, and it would be better for it to just not exist in the absence of data (with an error message).
So here's my plan: I can add a prop to the component to pass in data from a parent rather than keep it as internal state only. So if data is passed, no fetch request in the component is necessary. From there what I can do is take the static bundle output of the app and, when the page is requested, the server will request the proper data just as the component would. Once the data is received, the server can grab the component from the bundle with regex, add the data as a prop, render the component, and stick it back in with the rest of the already rendered static content.
Is this the correct way to do this? It feels a bit complicated, but that might just be how it's done. I'm not sure.
Your intuitions are correct. In current React (17.0), it's quite cumbersome to do SSR with data-fetching inside components.
What needs to be achieved on a conceptual level is that all data dependencies need to be known upfront, ie. before calling ReactDOM's render. This way, one can access the data in a synchronous manner which allows to do one-pass render on a server.
I don't quite follow your idea to "grap the component from the bundle with regex". One way of solving the data dependency problem is to inject the data into React tree from the root component (ie. ReactDOM.renderToString(<App componentStaticData={data} />)) and make the data-dependent component aware of the fact that it can just grab the data from there instead of doing (asynchronous) call. It's important to note that useEffects are not executed on the server.
Another idea to grab all the data dependencies is to do two-pass render. First one is used as a way to collect all resources used, then we await their completion and inject them as static data into send pass.
Third way is to use one of the React frameworks that provide SSR out of the box. You can have a look at (among many others) Next.js or Gatsby. Depending on your setup, this might be easiest or the hardest way to achieve SSR.
The simple Code Sandbox below shows that navigating to a component triggers a fresh render. The React profiler states the components were rendered because they are continually seen for the first time. I understand this to be the correct React behavior, because each mounting/render generates new method references that triggers the re-rendering. However, this means that several http requests will be made despite nothing having changed. Is there any way to keep those requests from being made every time the user navigates to a component?
In the codesandbox I've tried using React.memo along with useCallback, and neither reuse the initial rendering.
Click the console tab to see each time the component renders, it makes a fresh ajax call.
https://codesandbox.io/s/github/pstephenwille/react-render-question
To avoid redundant HTTP calls you have to verify that your origin server does not have updated your local data. If you really want to just fire the HTTP calls once, you can e.g. execute them in your App component or pass a state and a callback function down to your Foo and Bar screen components that will ensure a singular call
I've seen this question repeated a lot of times, but I'm trying to develop a react/redux app and every time i tried to fetch data before the page is loaded i got a lot of problems.
I feel the problem is the async calls, looks that sometimes when i use the data result of the data fetch, the store hasn't upload yet, so i try to access to undefined variables and i get error.
I've tried to use componentDidMount() and from inside the method trigger an action that fetch the data and actualize the store state for me . I also tried to use componentDidMount() and then componentWillReceiveProps(nextProps) to use the data when i cannot directly use it in the html.
So the problem is that sometimes, not always, the data i want to fetch is already undefined when i use it. Is there a way to avoid that? And How is the correct way to fetch data?
Have you tried using componentWillMount? Everything you put inside componentWillMount will execute before your application gets rendered, unlike componentDidMount where first you get your application rendered, then it executes everything you put inside.
Also, have you every looked at hooks and useEffect function? Try to research hooks cause it will solve a lot of boilerplate with standard lifecycle functions.
I'm new to React and I'm trying to figure out the best way to request information from the server based on the URL. I'm using Redux and React Router v4.
Let's say I have a route /foo/:id, and a component Foo that will render something based on id. However Foo needs some server data related to id to do so. I believe the way to accomplish this would be to use mapDispatchToProps to create a function that takes id as input, does some async work, dispatches an action, and ultimately updates the redux state.
My question is: where is the most appropriate place to invoke the dispatch? In this scenario, there's no form submission or button click to kick things off. Originally I was thinking of including a check for the id data in render() and fetching if it was not populated, but this felt wrong due to the side effects.
You can do it in componentDidMount of the Foo component, similar to this example from the Redux GitHub project.
Your intuition is right that render is not a good place to do so. Most people do it in the componentDidMount lifecycle method of the component.
On a relevant note, you will also want to do fetching also in the componentWillReceiveProps method like what they did here. Reason being if your user navigated from foo/1/ to foo/2/, the component is already on the screen and will not be mounted again, hence componentDidMount will not be called again. The fetching for the second user will be done in the componentWillReceiveProps method.
i think the best way to do the dispatch inside the componentWillReceiveProps() which would help you fetch some data before the component renders
It seems your use case is well-captured by the react-refetch package which you can find here. It provides a higher-order component that allows you to specify dependencies at specific API endpoints and then resolves them when a new instance of your component is created.
Importantly it injects the data into your components props using a synchronous abstraction of a promise called a PromiseState. This will allow you to conditionally render your component depending on whether the data is say pending, fulfilled, rejected, etc.
This is not attached in any way to Redux, it skips that layer entirely, so do keep it in mind that the response is directly injected into the component and does not go through your redux store's state.
I am using the server side rendering to the app, I want to load some data through the API before the page renders the at server side.
As I found the componentWillMount is called before rendering both server side and client side, but it doesn't executing the ajax code. Says $ is undefined,
Please anyone tell me what is the way to load the data before rendering the page.
This will be a generic answer because of the lack of details in the question. But I hope it sheds some light on the subject.
Based on my personal experience, if you're server rendering react you better have a "Controller" out-most component, that would deal with the whole state and everything beneath it should be mostly controlled or not depend on Ajax to load data at all. If you're using Redux, for instance, this "controller" component would be the Provider. This is because it's much easier for 1 component to deal with server vs. client than all of them.
Now, the outer "controller" component should know whether or not it's on the server and load data differently accordingly. One way to do that is to check if "window" is defined. That is: If you're on the server, don't use Ajax al all to load the initial state.. But if you are on the client, then use Ajax.
Now you have one last problem. When you do server rendering, you don't want the client to round-trip to the server to get the same data it already has. In order to prevent that, render a script block to be executed on the client, that contains the initial state and DO NOT do the Ajax if this variable is set. Like this:
<script>
window._serverState = {...}
</script>
Another thing.. I personally don't use $ for Ajax I don't think React and jQuery should be used together. Check out Axios.
Generally It is not advisable to make ajax call in componentWillMount. Because just after the componentWillMount rendering happens so rendering has to wait till the ajax call gets completed and gets new data.
It's better to get the rendering done for the first time with empty state data lets say
data:[]
and make ajax call in componentDidMount, this is the place where you can perform dom manipulation and send ajax request.
componentDidMount() {
sendAjaxRequest()
.then(
(newDataFromServer) => {
this.setState({data : newDataFromServer });
});
}
After new data is fetched from server set the state with new data
this.setState({data:newDataFromServer});
This will cause the re-rendering to happen with latest data fetched from server and new state changes will be reflected.