We just start moving into React Hooks from the React life-cycle and one of the things that we noticed, is that there's no straightforward way to run something similar to ComponentWillMount for loading data before sending the rendered page to the user (since useEffect is not running on SSR).
Is there any easy way supported by React to do so?
you might want to check react.lazy
I think you didn't get me 馃ぃ
Talking about server side rendering and data fetching
const [loading, setLoading] = useState(false)
const [data, setData] = useState()
useEffect(() => {
setLoading(true)
fetch(opt)
.then(result => {
setData(result)
setLoading(false)
})
.catch(() => {})
}, [])
emmm, my english is not that good, may be something like this?
Thank you for the suggestion, but useEffect is not running on server side :(
Essentially, you have to implement a custom hook that is capable of reading data from cache:
function useData(url) {
let [, setDummy] = React.useState(false);
React.useEffect(() => {
cache.fetch(url).then(() => { setDummy(val => !val) });
}, [url]);
return cache.get(url);
}
When you have something like this in place, on your server you need to populate your cache with relevant data before rendering:
await Promise.all([cache.fetch(url1), cache.fetch(url2)]);
Then you need to serialize your cache and send to the browser, so your app could be hydrated properly.
Well, it's kind of a solution, but I wonder if there's a more straightforward way to do it.
I feel it imperative to point out that doing data-fetching within React components is an unhealthy practice.
Sure, nothing stops you from doing that, but conceptually React is about transforming data into a view, so fetching data should come before you hit React.
Not only you can fetch from a react component, but there are multiple examples out there of how to test such components. Yet again, if you care about your testing strategy before you write the code, you'll see multiple benefits in keeping data fetching out of the view layer. (If you ever worked with Angular I, you may be able to recall seeing ngResource
in controllers and the catastrophic consequences this had further down the line.)
@Izhaki can you point to some of these examples?
It's not talking about server side rendering in any way tho..
Something to thinker of: If you are to do any of the following:
How will these marry with data fetching from a component that may return different values every time?
@Izhaki How is that related to the question?
It's not talking about server side rendering in any way tho..
Sorry, some misunderstanding on my side - I thought you're asking for examples for testing fetch within components.
If you're looking for how to do this (not within components), just google "server side rendering fetch". You should see a few examples using redux (although you don't have to use it), but at any case so long the fetch happens outside React (ie, useEffect
) you're locking the event loop and you're off the hook (metaphorically and literally)... kind of support to what I've said: "doing data-fetching within React components is an unhealthy practice."
Then to actual specifics, if using redux you can fetch on the server, render the page, then pass the redux state to the client as initial state and then your client doesn't need to fetch at all (unless you do polling - but the initial data still comes for the server). I guess this still doesn't answer the original question, but that's how it's done "properly".
Ultimately, this redux solution is not much different from the other proposal - you have to pass the fetched data from the server to the client somehow so it hydrates properly. There is no other way around it.
I have write a custom hook that run on both client/server side. On server side, the hook collect all promise and wait for it resolve.
// useSSREffect.ts
import { useEffect, useContext } from 'react';
import { SRRContext } from '../context/SRRContext';
export function useSSREffect(handler, deps) {
if (__SERVER__) {
const context = useContext(SRRContext);
const promise = handler();
if (!!promise) {
context.pushPromiseToWait(promise);
}
} else {
useEffect(() => {
handler();
}, deps);
}
}
// DemoComponent.ts
export default function DemoComponent(){
useSSREffect(()=>{
return fetch("/api/data");
}, []);
return <div/>
}
In SSR code, you should write a render loop.
while ( has promise to wait ){
wait for all promise
render react app
}
Hi @idangozlan , check this repo: https://github.com/ilkeraltin/react-ssr-news . See how @ilkeraltin exports components (with loadData function) and wraps in Promise.all in the server.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contribution.
there's no straightforward way to run something similar to ComponentWillMount for loading data before sending the rendered page to the user (since useEffect is not running on SSR).
This sounds a bit confused to me. componentWillMount
never worked for data fetching either because it is synchronous. So there is no way you could use it to start fetching things before either.
If componentWillMount
somehow worked for you (by synchronously filling the cache?) then you should be able to do the same thing in the component body directly.
In longer term, we plan to have a solution for this. But this problem is entirely unrelated to whether or not you use Hooks.
@gaearon yeah, this is exactly what we did, thanks!
Dear @mduclehcm
can u explain please ur context?
what is pushPromiseToWait
?
Dear @mduclehcm
can u explain please ur context?
what ispushPromiseToWait
?
please check your email!
Dear @mduclehcm
can you explain please your context?
what is pushPromiseToWait?
can you share full code here?
Most helpful comment
This sounds a bit confused to me.
componentWillMount
never worked for data fetching either because it is synchronous. So there is no way you could use it to start fetching things before either.If
componentWillMount
somehow worked for you (by synchronously filling the cache?) then you should be able to do the same thing in the component body directly.In longer term, we plan to have a solution for this. But this problem is entirely unrelated to whether or not you use Hooks.