⚠️ 【Draft – Pending Review】
Stop writing useEffect for data fetching. Use Request Strategies instead.
I’ve been a React developer for about three years. And for most of that time, I wrote data fetching the same way everyone else did:
useEffect(() => {
fetch('/api/users')
.then(res => res.json())
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, []);
It works. But it’s not good.
Every list page. Every search box. Every filter. Same pattern. And the code kept growing — loading states, error handling, debounce timers, race condition flags. A simple user list with search would easily hit 80 lines of boilerplate.
Then I found alova, and I realized: I’d been fighting problems that didn’t need to exist.
The Before: useEffect Hell
Here’s what my code used to look like:
// 🔴 Before: 80 lines of manual state management
import { useState, useEffect, useCallback } from 'react';
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [keyword, setKeyword] = useState('');
const [page, setPage] = useState(1);
const fetchUsers = useCallback(async () => {
setLoading(true);
setError(null);
try {
const res = await fetch(`/api/users?keyword=${keyword}&page=${page}&pageSize=10`);
const data = await res.json();
setUsers(data.list);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}, [keyword, page]);
useEffect(() => { fetchUsers(); }, [fetchUsers]);
// Manual debounce
useEffect(() => {
const t = setTimeout(() => fetchUsers(), 500);
return () => clearTimeout(t);
}, [keyword]);
// Manual race condition handling
useEffect(() => {
let cancelled = false;
fetch(`/api/users?keyword=${keyword}&page=${page}`)
.then(res => res.json())
.then(data => { if (!cancelled) setUsers(data.list); });
return () => { cancelled = true; };
}, [keyword, page]);
if (loading) return <Spinner />;
if (error) return <Error msg={error} />;
return (
<div>
<SearchInput value={keyword} onChange={setKeyword} />
<UserList data={users} />
</div>
);
}
This code has several issues:
- Too many manual states — loading, error, data all declared by hand
- DIY debounce — setTimeout/clearTimeout everywhere, easy to mess up
- Race conditions — the classic cancelled flag pattern, error-prone
- Dependency chains — useCallback + useEffect, changing one prop means tracing the whole chain
I wrote this pattern dozens of times. Every new project, same boilerplate.
The After: Request Strategies with alova
Then I switched to alova, and the same component became this:
// 🟢 After: 15 lines with alova request strategies
import { useRequest, useWatcher } from 'alova/client';
import { alovaInstance } from './api';
function UserList() {
const [keyword, setKeyword] = useState('');
// List data — auto-managed loading/data/error
const { loading, data: users = [], error } = useRequest(
() => alovaInstance.Get('/api/users', {
params: { page: 1, pageSize: 10 }
}),
{ initialData: [] }
);
// Search — debounce + race condition handling built in
const { data: searchResult = [] } = useWatcher(
() => alovaInstance.Get('/api/users', {
params: { keyword, page: 1, pageSize: 10 }
}),
[keyword],
{ debounce: 500 }
);
if (loading) return <Spinner />;
if (error) return <Error msg={error.message} />;
return (
<div>
<SearchInput value={keyword} onChange={setKeyword} />
<UserList data={users} />
</div>
);
}
This isn’t magic. It’s design.
alova transforms data fetching from imperative to declarative. Instead of telling the computer how to fetch, you tell it what to fetch — and the library handles the rest.
| What you used to write by hand | What alova does for you |
|---|---|
3x useState for loading/data/error |
useRequest returns them automatically |
useEffect + useCallback chains |
Declarative watchers with auto dependency management |
setTimeout / clearTimeout for debounce |
debounce: 500 — one line |
cancelled flag for race conditions |
Built-in abort on next request |
| Manual URL parameter construction |
params object, auto-serialized |
What Are “Request Strategies”?
alova isn’t a simple fetch wrapper. It’s a strategy-based request library that covers the most common data-fetching patterns in frontend development:
| Strategy Hook | You tell it | It handles |
|---|---|---|
useRequest |
What to fetch | loading, data, error, send, abort |
useWatcher |
What state to watch | debounce, auto-refetch, race conditions |
usePagination |
Page config | Page state, preloading, optimistic updates |
useForm |
Form data | Draft persistence, auto-submit, reset |
useAutoRequest |
Polling config | Auto-polling, focus refresh, reconnect |
useCaptcha |
Phone number | Countdown timer, auto-send |
useSerialRequest |
Dependent calls | Pass previous result to next request |
useRetriableRequest |
Retry config | Exponential backoff with jitter |
Each hook maps to a pattern you’ve probably implemented dozens of times. The goal isn’t to write less code — it’s to not write it at all.
The Results
After switching to alova across my projects, here’s what I’ve seen:
- 60% less boilerplate — pages went from ~150 lines to ~60 lines
- Fewer bugs — no more “forgot to clearTimeout” or “wrong cancelled flag”
- Faster onboarding — new devs learn “use this hook for this pattern” instead of debugging useEffect chains
- Better code review — request logic is declarative and self-documenting
Getting Started
npm install alova
Then pick an adapter:
npm install @alova/fetch # For browsers / Node.js
npm install alova/axios # If you're on axios
And write your first strategic request:
import { createAlova } from 'alova';
import fetchAdapter from '@alova/fetch';
import ReactHook from 'alova/react';
export const alovaInstance = createAlova({
baseURL: 'https://api.example.com',
statesHook: ReactHook,
requestAdapter: fetchAdapter(),
responded: res => res.json()
});
// In your component:
function Profile() {
const { loading, data, error } = useRequest(
() => alovaInstance.Get('/user/profile'),
{ initialData: {} }
);
// ...
}
Stop writing useEffect for data fetching. Your time is better spent on actual product logic.
Give alova a try. The docs are at alova.js.org, the source is on GitHub, and the community is welcoming.
Using alova v3. Tested in React, Vue, and Svelte.