Complex Loader Management Hook for React.
Read the Medium post "Managing Complex Waiting Experiences on Web UIs".
react-wait is a React Hook helps to manage multiple loading states on the page without any conflict. It's based on a very simple idea that manages an Array
of multiple loading states. The built-in loader component listens its registered loader and immediately become loading state.
React has its own Suspense feature to manage all the async works. For now it only supports code-splitting (not data-fetching).
useWait
allows you to manage waiting experiences much more explicitly and not only for Promised/async patterns but also complete loading management.
Here's a quick overview that what's useWait
for:
import { useWait, Waiter } from "react-wait";
function A() {
const { isWaiting } = useWait();
return (
<div>
{isWaiting("creating user") ? "Creating User..." : "Nothing happens"}
</div>
);
}
function B() {
const { anyWaiting } = useWait();
return (
<div>
{anyWaiting() ? "Something happening on app..." : "Nothing happens"}
</div>
);
}
function C() {
const { startWaiting, endWaiting, isWaiting } = useWait();
function createUser() {
startWaiting("creating user");
// Faking the async work:
setTimeout(() => {
endWaiting("creating user");
}, 1000);
}
return (
<button disabled={isWaiting("creating user")} onClick={createUser}>
<Wait on="creating user" fallback={<Spinner />}>
Create User
</Wait>
</button>
);
}
ReactDOM.render(
<Waiter>
<C />
</Waiter>,
document.getElementById("root")
);
If you are a try and learn developer, you can start trying the react-wait now using codesandbox.io.
yarn add react-wait
import { Waiter, useWait } from "react-wait";
function UserCreateButton() {
const { startWaiting, endWaiting, isWaiting, Wait } = useWait();
return (
<button
onClick={() => startWaiting("creating user")}
disabled={isWaiting("creating user")}
>
<Wait on="creating user" fallback={<div>Creating user!</div>}>
Create User
</Wait>
</button>
);
}
And you should wrap your App
with Waiter
component. It's actually a Context.Provider
that provides a loading context to the component tree.
const rootElement = document.getElementById("root");
ReactDOM.render(
<Waiter>
<App />
</Waiter>,
rootElement
);
$ yarn add react-wait
# or if you using npm
$ npm install react-wait
react-wait provides some helpers to you to use in your templates.
Returns boolean value if any loader exists in context.
const { anyWaiting } = useWait();
return <button disabled={anyWaiting()}>Disabled while waiting</button>;
Returns boolean value if given loader exists in context.
const { isWaiting } = useWait();
return (
<button disabled={isWaiting("creating user")}>
Disabled while creating user
</button>
);
Starts the given waiter.
const { startWaiting } = useWait();
return <button onClick={() => startWaiting("message")}>Start</button>;
Stops the given waiter.
const { end } = useWait();
return <button onClick={() => endWaiting("message")}>Stop</button>;
function Component() {
const { Wait } = useWait();
return (
<Wait on="the waiting message" fallback={<div>Waiting...</div>}>
The content after waiting done
</Wait>
);
}
Better example for a button
with loading state:
<button disabled={isWaiting("creating user")}>
<Wait on="creating user" fallback={<div>Creating User...</div>}>
Create User
</Wait>
</button>
With reusable loader components, you will be able to use custom loader components as example below. This will allow you to create better user loading experience.
function Spinner() {
return <img src="spinner.gif" />;
}
Now you can use your spinner everywhere using waiting
attribute:
<button disabled={isWaiting("creating user")}>
<Wait on="creating user" fallback={<Spinner />}>
Create User
</Wait>
</button>
To keep your code DRY you can create a Waiting Context
using createWaitingContext
.
function CreateUserButton() {
const { createWaitingContext } = useWait();
// All methods will be curried with "creating user" on.
const { startWaiting, endWaiting, isWaiting, Wait } = createWaitingContext(
"creating user"
);
function createUser() {
startWaiting();
setTimeout(endWaiting, 1000);
}
return (
<Button disabled={isWaiting()} onClick={createUser}>
<Wait fallback="Creating User...">Create User</Wait>
</Button>
);
}
- Fatih Kadir Akın, (creator)
Since react-wait based on a very simple idea, it can be implemented on other frameworks.
- vue-wait: Multiple Process Loader Management for Vue.
- dom-wait: Multiple Process Loader Management for vanilla JavaScript.
MIT © Fatih Kadir Akın