
Yes, it happened again, although I had a clear goal in mind for the week, I ended up leaving my current projects aside to make one more thing (plus got a ton of stuff I needed to work on). This time I made an NPM module, nothing super fancy but something I believe will be pretty useful in some cases. I present you use-async-view. A simple (says who made it), easy to implement hook for managing the state of asynchronous views in your React and React Native applications.
In a way my ‍
‍hook is pretty similar to React’s new Suspense component which “lets you display a fallback until its children have finished loading” with the difference that I allow you to display a fallback, a loading, and an error components with the addition of a reload function. Plus this was made for both React and React Native so there’s that. Anyway let’s jump to how the hook works!
TL;DR
use-async-view is a tiny hook for “fetch something, then show the right screen” flows.
- You pass it:
- an async loadFn
- components for each state (Fallback, Loading, Success, Error)
- It gives you:
- a RenderedView element you can drop straight into your JSX
- a reload function to re-run the request
- an internal state machine (idle → loading → success | error) so you don’t have to re-write it in every component
Use it when you want simple async views without pulling in heavier data-fetching libraries.
Quickstart
As any other node module, you need to start by getting it installed, for that the command thankfully is really easy, just do the following:
npm i use-async-view
# orp
npm add use-async-viewOkay, I’ve installed it in my project, now what? well to be completely honest this would be literally the bare minimum.
import { useAsyncView } from 'use-async-view';
type Data = { name: string };
//Get your async function
const LoadData = async () => {
return fetch('https://dummyjson.com/products?delay=2000')
.then(res => res.json());
}
//The component you want to use to render the data
const cards = ({ data }) => {
return data.products.map((product) => (
<div key={product.id} className="card">
<h3>{product.title}</h3>
<p>{product.description}</p>
</div>
));
}
//use it!
export default function App() {
const { RenderedView, reload } = useAsyncView({
loadFn: LoadData,
Success: cards,
});
return (
<div>
<h1>Initial Load</h1>
{RenderedView}
<button onClick={reload}>Reload</button>
</div>
);
}Pretty simple right? no state management needed, you pass an async function and the component you want to use to display the response, easy as that, by default the hook will manage an error, loading, and response states. and the setup above is the bare minimum you need to do, just the component and a function, now this will yield you the following:

And this is what it will show if there was an issue loading the information:

Now, as I mentioned above, this is the minimum setup for the hook to work! there are multiple things you can add to it, let me explain them all:
//Declaring the hook
const { RenderedView, reload } = useAsyncView({
loadFn: () => fetch('https://dummyjson.com/products?delay=2000').then(res => res.json()), //Async function returning a payload
Fallback: () => <button onClick={reload}>Load user</button>, // Component to render while in idle state
Loading: () => <Spinner />, //Component to show while the function is "Loading"
Success: ({ data }) => <UserCard user={data} />, //Component to render on success, receives loaded data
auto: false, //Defines whether to automatically trigger the load on mount, true by default
});‍With this, you don’t have to bother with handling the state of the information that is being loaded through your parent view or to be concerned with re-renders being caused by the state changing. everything is already handled for you!
What else is included?
When I made this Module I included a couple of additional components for people to use, just to make it easier to reuse between codebases, one of them is the LoadingView which was made to be a flexible loading component to manage reusability, here’s how to use it along with it’s different options:
import { LoadingView } from 'use-async-view';
// Basic usage
<LoadingView />
// With custom text
<LoadingView text="Loading your data..." />
// With custom loader
<LoadingView
text="Please wait..."
loader={<MyCustomSpinner />}
/>
// Show loader explicitly
<LoadingView showLoader={true} text="Loading..." />And finally  an ErrorView component, same case as for the LoadingView:
import { ErrorView } from 'use-async-view';
// Basic usage
<ErrorView message="Failed to load data" />
// With error details
<ErrorView
message="Failed to load data"
error={error}
/>
// With retry function
<ErrorView
message="Failed to load data"
error={error}
runFunction={handleRetry}
/>‍Why choose this over the usual approaches?
To be clear: I’m not saying you must use this hook. It’s just one more option in your toolbox. and I believe it could more than definitely help and I’d be really happy to know this can make other people’s projects easier to handle but what you use in your project is up to you. what I can say is that this hook could help you out for the following points:
- There is no need for re-building the same state machine every time you need to load data.
- Easily manage the “view per state” via components, keeping your project reusable and seamless.
- One place to wire reload, errors, and display logic.
It’s not a replacement for full data-fetching libraries, it’s more for those “I just need to load something and show the right view” moments.
Anything to add?
- Cancellation: Do you abort in-flight calls on unmount or overlapping
reloads? - Dependencies: None! this project uses just React’s basic hooks to work, no additional dependencies
- Concurrency: IF someone tries to spam the reload function there is a guard that will prevent it, also the hook stops any calls on unmount
- SSR: This component is not thought for Server Side rendering
- Status values: Exact strings returned by
status(e.g.,"idle" | "loading" | "success" | "error").
When not to use it
If you need caching, deduping, infinite scrolling, or optimistic updates, use react-query/SWR. use-async-view shines for simple “fetch then show a view” moments.


