DEV Community

purposewalker
purposewalker

Posted on

React custom hooks

Stop Rewriting the Same React Logic — I Built Hookraft to Fix This
Every React project I've worked on has the same problem.
You open a new component and before you write a single line of real logic, you're already typing this:
tsxconst [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
const [data, setData] = useState(null)
Then the useEffect. Then the try/catch. Then the cleanup. Then you do it again in the next component. And the next project. And the next.
I got tired of it. So I built Hookraft.

What is Hookraft?
Hookraft is a collection of composable React hooks that handle the logic side of your app — async flows, task queues, state history, and more.
The idea is simple: instead of scattering useState and useEffect across your components, you use hooks that already know how to handle the hard parts.

hookraft.site | github.com/purposewalks9/Hookraft

The two hooks shipping today

  1. useQueue — process tasks in sequence Ever needed to send a bulk list of emails, upload multiple files, or process a list of items one by one? Without Hookraft you're managing a queue manually — tracking which items are pending, which are processing, which failed, and wiring up concurrency yourself. With useQueue: tsximport { useQueue, Queue } from "@hookraft/usequeue"

function BulkEmailSender() {
const queue = useQueue({
onProcess: async (item) => await sendEmail(item.email),
onSuccess: (item) => console.log(Sent to ${item.email}),
onError: (item, err) => console.error(Failed: ${item.email}),
onDone: () => toast("All emails sent!"),
concurrency: 2,
})

return (


queue.add({ email: "a@example.com" })}>
Add Email


Start


Pause
  <p>Pending: {queue.pending.length}</p>
  <p>Processing: {queue.processing.length}</p>
  <p>Completed: {queue.completed.length}</p>
  <p>Failed: {queue.failed.length}</p>

  <Queue when={queue.status} fallback={<p>Queue is idle</p>}>
    <p>Queue is active...</p>
  </Queue>
</div>

)
}
You get:

Full lifecycle control (idle → running → paused → done)
Concurrency support
Per-item success and error callbacks
A Queue JSX component that renders based on status

No manual state juggling. Just describe the flow.

  1. useHistory — undo/redo any state in one line This one is my favourite. Every app that lets users create or edit anything should have undo/redo. But building it from scratch means reducers, past/future stacks, careful immutability — it's a lot. With useHistory: tsximport { useHistory } from "@hookraft/usehistory"

function TextEditor() {
const {
state,
set,
undo,
redo,
canUndo,
canRedo,
jump,
history,
clear,
} = useHistory("", { limit: 100 })

return (


value={state}
onChange={(e) => set(e.target.value)}
placeholder="Start typing..."
/>
  <button onClick={undo} disabled={!canUndo}>Undo</button>
  <button onClick={redo} disabled={!canRedo}>Redo</button>
  <button onClick={clear}>Clear</button>

  {/* Click any past entry to jump straight to it */}
  <ul>
    {history.map((entry, index) => (
      <li key={index}>
        <button onClick={() => jump(index)}>
          {entry || "(empty)"}
        </button>
      </li>
    ))}
  </ul>
</div>

)
}
The jump(index) function is the secret weapon here. Instead of clicking undo 10 times, you can render a full history timeline and let users teleport to any point — like Google Docs version history or Figma's history panel.
Works with any type — strings, objects, arrays, whatever your state is.

The philosophy behind it
Modern React apps are not just UI. They are systems of:

Async operations
State transitions
Background tasks
Error recovery

Most developers solve these problems repeatedly with ad-hoc logic. Over time that creates fragile, hard-to-maintain codebases.
Hookraft's goal is simple:

Turn complex logic into readable, predictable primitives.

Instead of stitching logic together, you define flows. Your components stay clean. Your logic stays structured.

Installing
bash# Queue hook
pnpm add @hookraft/usequeue

History hook

pnpm add @hookraft/usehistory
Both packages are fully typed with TypeScript, work with React 19, and have zero UI opinions — use them with any component library or none at all.

What's coming next
Hookraft is actively being built. On the roadmap:

useAsync — async function with full status tracking
useRetry — auto-retry with backoff
usePoll — interval-based polling with pause/resume
useStepFlow — multi-step wizard flow
useForm — form state and validation

Try it out

Site: hookraft.site
GitHub: github.com/purposewalks9/Hookraft
Twitter: @purpose_walker

If you find it useful, a star on GitHub goes a long way. And if you have a hook idea or want to contribute, PRs are open.
What logic do you find yourself rewriting the most in React? Drop it in the comments it might become the next Hookraft hook

Top comments (1)

Collapse
 
purposewalks9 profile image
purposewalker

nice