useRecoilValue #
什么是 useRecoilValue? #
useRecoilValue 是一个只读 Hook,用于获取 Recoil 状态的值。当组件只需要读取状态而不需要修改时,使用这个 Hook 可以获得更好的性能。
jsx
const value = useRecoilValue(state);
基本用法 #
读取 Atom #
jsx
import { atom, useRecoilValue } from 'recoil';
const countState = atom({
key: 'countState',
default: 0,
});
function CountDisplay() {
const count = useRecoilValue(countState);
return <div>Current count: {count}</div>;
}
读取 Selector #
jsx
import { atom, selector, useRecoilValue } from 'recoil';
const firstNameState = atom({
key: 'firstName',
default: 'John',
});
const lastNameState = atom({
key: 'lastName',
default: 'Doe',
});
const fullNameState = selector({
key: 'fullName',
get: ({ get }) => {
return `${get(firstNameState)} ${get(lastNameState)}`;
},
});
function NameDisplay() {
const fullName = useRecoilValue(fullNameState);
return <div>Full Name: {fullName}</div>;
}
性能优势 #
细粒度订阅 #
组件只订阅它使用的状态:
jsx
const userState = atom({
key: 'user',
default: {
name: 'John',
email: 'john@example.com',
age: 30,
},
});
function UserName() {
const user = useRecoilValue(userState);
return <span>{user.name}</span>;
}
function UserEmail() {
const user = useRecoilValue(userState);
return <span>{user.email}</span>;
}
避免不必要的重渲染 #
使用 useRecoilValue 而不是 useRecoilState,当只需要读取时:
jsx
function DisplayOnlyComponent() {
const count = useRecoilValue(countState);
return <div>{count}</div>;
}
对比使用 useRecoilState:
jsx
function LessOptimizedComponent() {
const [count, setCount] = useRecoilState(countState);
return <div>{count}</div>;
}
实战示例:统计信息 #
jsx
import { atom, selector, useRecoilValue } from 'recoil';
const todoListState = atom({
key: 'todoList',
default: [
{ id: 1, text: 'Learn Recoil', completed: true },
{ id: 2, text: 'Build an app', completed: false },
{ id: 3, text: 'Write tests', completed: false },
],
});
const todoStatsState = selector({
key: 'todoStats',
get: ({ get }) => {
const list = get(todoListState);
const total = list.length;
const completed = list.filter(item => item.completed).length;
const active = total - completed;
const percentComplete = total === 0 ? 0 : (completed / total) * 100;
return { total, completed, active, percentComplete };
},
});
function TodoStats() {
const stats = useRecoilValue(todoStatsState);
return (
<div>
<p>Total: {stats.total}</p>
<p>Active: {stats.active}</p>
<p>Completed: {stats.completed}</p>
<p>Progress: {stats.percentComplete.toFixed(1)}%</p>
</div>
);
}
实战示例:过滤列表 #
jsx
import { atom, selector, useRecoilValue, useRecoilState } from 'recoil';
const productsState = atom({
key: 'products',
default: [
{ id: 1, name: 'Laptop', category: 'electronics', price: 999 },
{ id: 2, name: 'T-Shirt', category: 'clothing', price: 29 },
{ id: 3, name: 'Phone', category: 'electronics', price: 699 },
{ id: 4, name: 'Jeans', category: 'clothing', price: 59 },
],
});
const filterState = atom({
key: 'filter',
default: 'all',
});
const filteredProductsState = selector({
key: 'filteredProducts',
get: ({ get }) => {
const filter = get(filterState);
const products = get(productsState);
if (filter === 'all') return products;
return products.filter(p => p.category === filter);
},
});
const productCountState = selector({
key: 'productCount',
get: ({ get }) => get(filteredProductsState).length,
});
function ProductList() {
const products = useRecoilValue(filteredProductsState);
const count = useRecoilValue(productCountState);
return (
<div>
<p>Showing {count} products</p>
<ul>
{products.map(product => (
<li key={product.id}>
{product.name} - ${product.price}
</li>
))}
</ul>
</div>
);
}
function FilterControls() {
const [filter, setFilter] = useRecoilState(filterState);
return (
<select value={filter} onChange={(e) => setFilter(e.target.value)}>
<option value="all">All</option>
<option value="electronics">Electronics</option>
<option value="clothing">Clothing</option>
</select>
);
}
与其他 Hook 对比 #
| Hook | 读取 | 写入 | 订阅更新 |
|---|---|---|---|
useRecoilState |
✓ | ✓ | ✓ |
useRecoilValue |
✓ | ✗ | ✓ |
useSetRecoilState |
✗ | ✓ | ✗ |
useRecoilValueLoadable |
✓ | ✗ | ✓ |
异步状态 #
对于异步 Selector,可以使用 useRecoilValue 配合 Suspense:
jsx
import { Suspense } from 'react';
const userQueryState = selector({
key: 'userQuery',
get: async ({ get }) => {
const userId = get(userIdState);
const response = await fetch(`/api/users/${userId}`);
return response.json();
},
});
function UserProfile() {
const user = useRecoilValue(userQueryState);
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<UserProfile />
</Suspense>
);
}
多个状态读取 #
jsx
function Dashboard() {
const user = useRecoilValue(userState);
const notifications = useRecoilValue(notificationsState);
const settings = useRecoilValue(settingsState);
return (
<div>
<h1>Welcome, {user.name}</h1>
<p>You have {notifications.length} notifications</p>
<p>Theme: {settings.theme}</p>
</div>
);
}
条件渲染 #
jsx
function ConditionalDisplay() {
const isLoggedIn = useRecoilValue(isLoggedInState);
if (!isLoggedIn) {
return <div>Please log in</div>;
}
const user = useRecoilValue(userState);
return <div>Welcome, {user.name}</div>;
}
TypeScript 支持 #
tsx
import { atom, selector, useRecoilValue } from 'recoil';
interface User {
id: number;
name: string;
email: string;
}
const userState = atom<User | null>({
key: 'userState',
default: null,
});
const userNameState = selector<string>({
key: 'userName',
get: ({ get }) => {
const user = get(userState);
return user?.name ?? 'Guest';
},
});
function UserGreeting() {
const userName = useRecoilValue(userNameState);
return <h1>Hello, {userName}!</h1>;
}
完整示例:仪表盘 #
jsx
import { atom, selector, useRecoilValue } from 'recoil';
const salesDataState = atom({
key: 'salesData',
default: [
{ month: 'Jan', sales: 4000 },
{ month: 'Feb', sales: 3000 },
{ month: 'Mar', sales: 5000 },
{ month: 'Apr', sales: 4500 },
],
});
const totalSalesState = selector({
key: 'totalSales',
get: ({ get }) => {
const data = get(salesDataState);
return data.reduce((sum, item) => sum + item.sales, 0);
},
});
const averageSalesState = selector({
key: 'averageSales',
get: ({ get }) => {
const data = get(salesDataState);
const total = get(totalSalesState);
return total / data.length;
},
});
const maxSalesState = selector({
key: 'maxSales',
get: ({ get }) => {
const data = get(salesDataState);
return Math.max(...data.map(item => item.sales));
},
});
function DashboardStats() {
const total = useRecoilValue(totalSalesState);
const average = useRecoilValue(averageSalesState);
const max = useRecoilValue(maxSalesState);
return (
<div className="stats">
<div className="stat-card">
<h3>Total Sales</h3>
<p className="value">${total.toLocaleString()}</p>
</div>
<div className="stat-card">
<h3>Average Sales</h3>
<p className="value">${average.toLocaleString()}</p>
</div>
<div className="stat-card">
<h3>Best Month</h3>
<p className="value">${max.toLocaleString()}</p>
</div>
</div>
);
}
总结 #
useRecoilValue 的核心要点:
| 特点 | 说明 |
|---|---|
| 只读 | 不能修改状态 |
| 订阅 | 状态变化时组件重渲染 |
| 性能 | 比使用 useRecoilState 更优 |
| 适用 | 只需要读取状态的组件 |
下一步,让我们学习 useSetRecoilState,了解只写状态的 Hook。
最后更新:2026-03-28