useRecoilCallback #
什么是 useRecoilCallback? #
useRecoilCallback 是一个 Hook,用于在回调函数中命令式地访问 Recoil 状态。它允许你在不订阅状态的情况下读取和写入状态,非常适合用于事件处理、副作用操作和批量更新。
jsx
const callback = useRecoilCallback((params) => (args) => {
}, deps);
基本用法 #
读取状态 #
jsx
import { atom, useRecoilCallback } from 'recoil';
const countState = atom({
key: 'countState',
default: 0,
});
function LogButton() {
const logCount = useRecoilCallback(({ snapshot }) => () => {
const count = snapshot.getLoadable(countState).contents;
console.log('Current count:', count);
});
return <button onClick={logCount}>Log Count</button>;
}
写入状态 #
jsx
import { atom, useRecoilCallback } from 'recoil';
const countState = atom({
key: 'countState',
default: 0,
});
function BatchUpdateButton() {
const incrementMultiple = useRecoilCallback(({ set }) => () => {
for (let i = 0; i < 10; i++) {
set(countState, prev => prev + 1);
}
});
return <button onClick={incrementMultiple}>Increment 10 times</button>;
}
Callback 参数 #
jsx
useRecoilCallback(({ snapshot, set, reset, refresh, gotoSnapshot, transact_UNSTABLE }) => {
}, deps);
| 参数 | 说明 |
|---|---|
snapshot |
只读状态快照 |
set |
设置状态值 |
reset |
重置状态到默认值 |
refresh |
刷新 Selector 缓存 |
gotoSnapshot |
恢复到指定快照 |
transact_UNSTABLE |
批量事务更新 |
Snapshot API #
getLoadable #
同步获取状态值:
jsx
const logUser = useRecoilCallback(({ snapshot }) => () => {
const userLoadable = snapshot.getLoadable(userState);
if (userLoadable.state === 'hasValue') {
console.log('User:', userLoadable.contents);
}
});
getPromise #
异步获取状态值:
jsx
const fetchUser = useRecoilCallback(({ snapshot }) => async () => {
const user = await snapshot.getPromise(userState);
console.log('User:', user);
});
getNodes #
获取所有状态节点:
jsx
const logAllStates = useRecoilCallback(({ snapshot }) => () => {
const nodes = snapshot.getNodes_UNSTABLE();
for (const node of nodes) {
console.log(node.key, snapshot.getLoadable(node).contents);
}
});
实战示例:批量操作 #
jsx
import { atom, useRecoilCallback, useRecoilValue } from 'recoil';
const todoListState = atom({
key: 'todoList',
default: [],
});
function TodoActions() {
const todos = useRecoilValue(todoListState);
const markAllComplete = useRecoilCallback(({ set }) => () => {
set(todoListState, todos.map(todo => ({ ...todo, completed: true })));
}, [todos]);
const deleteCompleted = useRecoilCallback(({ set }) => () => {
set(todoListState, todos.filter(todo => !todo.completed));
}, [todos]);
const clearAll = useRecoilCallback(({ reset }) => () => {
reset(todoListState);
});
return (
<div>
<button onClick={markAllComplete}>Mark All Complete</button>
<button onClick={deleteCompleted}>Delete Completed</button>
<button onClick={clearAll}>Clear All</button>
</div>
);
}
实战示例:表单提交 #
jsx
import { atom, useRecoilCallback, useRecoilValue } from 'recoil';
const formState = atom({
key: 'form',
default: {
username: '',
email: '',
password: '',
},
});
const isSubmittingState = atom({
key: 'isSubmitting',
default: false,
});
function SubmitButton() {
const submitForm = useRecoilCallback(({ set, reset, snapshot }) => async () => {
set(isSubmittingState, true);
try {
const form = await snapshot.getPromise(formState);
const response = await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify(form),
});
if (response.ok) {
reset(formState);
alert('Form submitted successfully!');
}
} catch (error) {
console.error('Submit failed:', error);
} finally {
set(isSubmittingState, false);
}
});
return <button onClick={submitForm}>Submit</button>;
}
实战示例:数据同步 #
jsx
import { atom, useRecoilCallback } from 'recoil';
const userState = atom({
key: 'user',
default: null,
});
const lastSyncState = atom({
key: 'lastSync',
default: null,
});
function SyncButton() {
const syncData = useRecoilCallback(({ set, snapshot }) => async () => {
const user = await snapshot.getPromise(userState);
if (!user) return;
const response = await fetch(`/api/sync/${user.id}`);
const data = await response.json();
set(userState, data.user);
set(lastSyncState, new Date().toISOString());
});
return <button onClick={syncData}>Sync</button>;
}
实战示例:撤销/重做 #
jsx
import { atom, useRecoilCallback, useRecoilValue } from 'recoil';
const historyState = atom({
key: 'history',
default: [],
});
const historyIndexState = atom({
key: 'historyIndex',
default: -1,
});
const canvasState = atom({
key: 'canvas',
default: null,
});
function useHistory() {
const undo = useRecoilCallback(({ set, snapshot }) => async () => {
const history = await snapshot.getPromise(historyState);
const index = await snapshot.getPromise(historyIndexState);
if (index > 0) {
set(historyIndexState, index - 1);
set(canvasState, history[index - 1]);
}
});
const redo = useRecoilCallback(({ set, snapshot }) => async () => {
const history = await snapshot.getPromise(historyState);
const index = await snapshot.getPromise(historyIndexState);
if (index < history.length - 1) {
set(historyIndexState, index + 1);
set(canvasState, history[index + 1]);
}
});
const pushHistory = useRecoilCallback(({ set, snapshot }) => async (state) => {
const history = await snapshot.getPromise(historyState);
const index = await snapshot.getPromise(historyIndexState);
const newHistory = history.slice(0, index + 1);
newHistory.push(state);
set(historyState, newHistory);
set(historyIndexState, newHistory.length - 1);
set(canvasState, state);
});
return { undo, redo, pushHistory };
}
实战示例:条件更新 #
jsx
import { atom, useRecoilCallback } from 'recoil';
const balanceState = atom({
key: 'balance',
default: 1000,
});
function WithdrawButton({ amount }) {
const withdraw = useRecoilCallback(({ set, snapshot }) => async () => {
const balance = await snapshot.getPromise(balanceState);
if (balance < amount) {
alert('Insufficient funds');
return;
}
set(balanceState, balance - amount);
console.log(`Withdrew $${amount}. New balance: $${balance - amount}`);
});
return <button onClick={withdraw}>Withdraw ${amount}</button>;
}
事务更新 #
使用 transact_UNSTABLE 进行批量原子更新:
jsx
const batchUpdate = useRecoilCallback(({ transact_UNSTABLE }) => () => {
transact_UNSTABLE(({ set, get }) => {
const count = get(countState);
set(countState, count + 1);
set(lastUpdatedState, Date.now());
set(updateCountState, prev => prev + 1);
});
});
与 useEffect 结合 #
jsx
function DataFetcher({ userId }) {
const fetchData = useRecoilCallback(({ set, snapshot }) => async () => {
const response = await fetch(`/api/users/${userId}`);
const user = await response.json();
set(userState, user);
}, [userId]);
useEffect(() => {
fetchData();
}, [fetchData]);
return null;
}
TypeScript 支持 #
tsx
import { atom, useRecoilCallback, RecoilState } from 'recoil';
interface User {
id: number;
name: string;
}
const userState = atom<User | null>({
key: 'userState',
default: null,
});
function UserActions() {
const updateUser = useRecoilCallback(
({ set }) => (updates: Partial<User>) => {
set(userState, (prev) => prev ? { ...prev, ...updates } : null);
},
[]
);
return <button onClick={() => updateUser({ name: 'New Name' })}>Update</button>;
}
完整示例:购物车结账 #
jsx
import { atom, useRecoilCallback, useRecoilValue } from 'recoil';
const cartState = atom({
key: 'cart',
default: [],
});
const orderState = atom({
key: 'order',
default: null,
});
const isProcessingState = atom({
key: 'isProcessing',
default: false,
});
function CheckoutButton() {
const cart = useRecoilValue(cartState);
const checkout = useRecoilCallback(({ set, reset, snapshot }) => async () => {
if (cart.length === 0) {
alert('Cart is empty');
return;
}
set(isProcessingState, true);
try {
const total = cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
const response = await fetch('/api/checkout', {
method: 'POST',
body: JSON.stringify({ items: cart, total }),
});
const order = await response.json();
set(orderState, order);
reset(cartState);
alert(`Order placed! Order ID: ${order.id}`);
} catch (error) {
console.error('Checkout failed:', error);
alert('Checkout failed. Please try again.');
} finally {
set(isProcessingState, false);
}
}, [cart]);
return (
<button onClick={checkout} disabled={cart.length === 0}>
Checkout (${cart.reduce((sum, item) => sum + item.price * item.quantity, 0).toFixed(2)})
</button>
);
}
总结 #
useRecoilCallback 的核心要点:
| 特点 | 说明 |
|---|---|
| 命令式 | 不订阅状态,按需访问 |
| 批量操作 | 支持批量更新多个状态 |
| 异步支持 | 支持异步操作 |
| 事件处理 | 适合用于事件回调 |
下一步,让我们学习 useRecoilValueLoadable,了解异步状态处理。
最后更新:2026-03-28