Use `React.cache` on the client
Unanswered
Tomistoma posted this in #help-forum
TomistomaOP
I have (client) component that makes a number of fetch request. The first fetch request made is based off a value selected in a drop down, and the pending promise is placed into state and passed to a component that calls
Once this data returns, the second dropdown can be selected which will fire off 2 more requests, sequentially. However, the data returned by these 2 requests are NOT used for any UI and instead are used within a context, for a map. This is significant because it means I don't have any way of wrapping them in a suspense or error boundary since there is no component associated with them. I would really like to do that, though, because it's a particularly nice paradigm and I don't want to have a bunch of states floating around holding errors from the API if I could just have a component throw an error that is caught by a boundary. Unfortunately, you can't throw errors in a
This is where it becomes a challenge because the component needs to go and make these 2 fetch requests, but it also needs to call
Does anyone know a way of using
use
so I can render suspense and error boundaries around it. The response data populates a second dropdown.Once this data returns, the second dropdown can be selected which will fire off 2 more requests, sequentially. However, the data returned by these 2 requests are NOT used for any UI and instead are used within a context, for a map. This is significant because it means I don't have any way of wrapping them in a suspense or error boundary since there is no component associated with them. I would really like to do that, though, because it's a particularly nice paradigm and I don't want to have a bunch of states floating around holding errors from the API if I could just have a component throw an error that is caught by a boundary. Unfortunately, you can't throw errors in a
useEffect
so my only option is to move the code into a component. This is where it becomes a challenge because the component needs to go and make these 2 fetch requests, but it also needs to call
use
on them so the suspense and error boundaries work but you're not allowed to create a promise in the same component that uses them otherwise you get stuck in a render loop. The solution seems to be to use React.cache
on the functions that make the request, but as these are client components that just seems to be a no-op. I've got the choice of making many caches myself (which is tedious and ugly) or somehow splitting more of this up into client-server, but I'm not exactly sure how that will work. Does anyone know a way of using
React.cache
on the client?1 Reply
TomistomaOP
Below is example code (comments are hopefully clearer):
'use client';
const getCatchmentGeometry = async (areaTypeId, catchmentName) => {
// ...
}
const getSamplingPoints = async (geometry) => {
// ...
};
const AoiData = ({ areaId, catchmentName }) => {
// The issue lies here becase promise is created every render. `React.cache` and `useMemo` both fail
const geometryPromise = getCatchmentGeometry(areaId, catchmentName);
const geometryData = use(geometryPromise);
// ...use geometryData to update map...
const samplingPointsPromise = getSamplingPoints(geometryData);
const samplingPoints = use(samplingPointsPromise);
// don't need this component to be visible, it just holds logic
return null;
};
export const SearchTab = () => {
const [areaId, setAreaId] = useState(...); // selected via a dropdown
const [catchmentNamePromise, setCatchmentNamePromise] = useState(...);
const [selectedCatchment, setSelectedCatchment] = useState(...); // selected via second dropdown
return (
<div>
<Dropdown
// set promise when dropdown changes
onChange={(o) => setCatchmentNamePromise(getCatchmentNames(o))}
/>
{
catchmentNamePromise
?
.... // the second dropdown, wrapped in `Suspense` and `ErrorBoundary`. When this changes, `setSelectedCatchment` is called
: null
}
{ /*
I want to do it this way so I can wrap it in Suspense and Error boundaries.
I could do the logic in a `useEffect`, but then I need to state to for the error, etc, and
it gets messy and goes against the exact features we were given for this purpose (imo)
*/}
{
selectedCatchment
? <ErrorBoundary errorComponent={() => <></>}>
<Suspense fallback={<></>}>
<AoiData areaId={areaId} catchmentName={catchmentName} />
</Suspense>
</ErrorBoundary>
: null
}
</div>
);
};