性能优化 #

Recoil 的性能优势 #

Recoil 的设计本身就具有良好的性能特性:

  • 细粒度订阅:组件只订阅需要的状态
  • 自动缓存:Selector 自动缓存计算结果
  • 最小化重渲染:状态变化只影响订阅的组件

选择正确的 Hook #

只读状态使用 useRecoilValue #

jsx
function DisplayName() {
  const user = useRecoilValue(userState);
  return <div>{user.name}</div>;
}

只写状态使用 useSetRecoilState #

jsx
function UpdateButton() {
  const setUser = useSetRecoilState(userState);
  
  return (
    <button onClick={() => setUser({ name: 'New Name' })}>
      Update
    </button>
  );
}

对比 #

jsx
function BadExample() {
  const [user, setUser] = useRecoilState(userState);
  
  return (
    <button onClick={() => setUser({ name: 'New Name' })}>
      Update
    </button>
  );
}

function GoodExample() {
  const setUser = useSetRecoilState(userState);
  
  return (
    <button onClick={() => setUser({ name: 'New Name' })}>
      Update
    </button>
  );
}

状态拆分 #

拆分大对象 #

jsx
const userState = atom({
  key: 'user',
  default: {
    profile: { name: '', email: '' },
    settings: { theme: 'light', language: 'en' },
    preferences: { notifications: true },
  },
});

function UserName() {
  const user = useRecoilValue(userState);
  return <div>{user.profile.name}</div>;
}

优化后:

jsx
const userProfileState = atom({
  key: 'userProfile',
  default: { name: '', email: '' },
});

const userSettingsState = atom({
  key: 'userSettings',
  default: { theme: 'light', language: 'en' },
});

const userPreferencesState = atom({
  key: 'userPreferences',
  default: { notifications: true },
});

function UserName() {
  const profile = useRecoilValue(userProfileState);
  return <div>{profile.name}</div>;
}

使用 atomFamily #

jsx
const todoFamily = atomFamily({
  key: 'todo',
  default: (id) => ({ id, text: '', completed: false }),
});

function TodoItem({ id }) {
  const [todo, setTodo] = useRecoilState(todoFamily(id));
  
  return (
    <div>
      <input
        value={todo.text}
        onChange={(e) => setTodo({ ...todo, text: e.target.value })}
      />
    </div>
  );
}

Selector 缓存 #

自动缓存 #

jsx
const expensiveSelector = selector({
  key: 'expensive',
  get: ({ get }) => {
    const data = get(dataState);
    return data.map(item => expensiveTransform(item));
  },
});

缓存策略 #

jsx
const cachedSelector = selector({
  key: 'cached',
  get: ({ get }) => {
    return computeExpensiveValue(get(dataState));
  },
  cachePolicy_UNSTABLE: {
    eviction: 'lru',
    maxSize: 100,
  },
});

避免不必要的计算 #

条件返回 #

jsx
const optimizedSelector = selector({
  key: 'optimized',
  get: ({ get }) => {
    const data = get(dataState);
    
    if (data.length === 0) {
      return [];
    }
    
    return data.map(expensiveTransform);
  },
});

提前退出 #

jsx
const filteredSelector = selector({
  key: 'filtered',
  get: ({ get }) => {
    const items = get(itemsState);
    const filter = get(filterState);
    
    if (filter === 'all') {
      return items;
    }
    
    return items.filter(item => item.category === filter);
  },
});

组件优化 #

使用 React.memo #

jsx
const TodoItem = React.memo(function TodoItem({ id }) {
  const [todo, setTodo] = useRecoilState(todoFamily(id));
  
  return (
    <div>
      <input
        value={todo.text}
        onChange={(e) => setTodo({ ...todo, text: e.target.value })}
      />
    </div>
  );
});

拆分组件 #

jsx
function UserCard({ userId }) {
  const user = useRecoilValue(userQueryState(userId));
  
  return (
    <div>
      <UserAvatar user={user} />
      <UserInfo user={user} />
      <UserActions userId={userId} />
    </div>
  );
}

const UserAvatar = React.memo(function UserAvatar({ user }) {
  return <img src={user.avatar} alt={user.name} />;
});

const UserInfo = React.memo(function UserInfo({ user }) {
  return (
    <div>
      <h3>{user.name}</h3>
      <p>{user.email}</p>
    </div>
  );
});

function UserActions({ userId }) {
  const setUser = useSetRecoilState(userQueryState(userId));
  
  return (
    <div>
      <button onClick={() => setUser(prev => ({ ...prev, followed: true }))}>
        Follow
      </button>
    </div>
  );
}

批量更新 #

使用 useRecoilCallback #

jsx
function BatchUpdateButton() {
  const updateAll = useRecoilCallback(({ set }) => () => {
    set(firstNameState, 'John');
    set(lastNameState, 'Doe');
    set(emailState, 'john@example.com');
  });
  
  return <button onClick={updateAll}>Update All</button>;
}

使用 transact_UNSTABLE #

jsx
const batchUpdate = useRecoilCallback(({ transact_UNSTABLE }) => () => {
  transact_UNSTABLE(({ set }) => {
    set(firstNameState, 'John');
    set(lastNameState, 'Doe');
    set(emailState, 'john@example.com');
  });
});

异步优化 #

预取数据 #

jsx
function usePrefetch(queryState) {
  return useRecoilCallback(({ snapshot }) => async () => {
    await snapshot.getPromise(queryState);
  });
}

function UserListItem({ userId }) {
  const prefetch = usePrefetch(userQueryState(userId));
  
  return (
    <div onMouseEnter={prefetch}>
      User {userId}
    </div>
  );
}

并行请求 #

jsx
const parallelQueryState = selector({
  key: 'parallelQuery',
  get: async ({ get }) => {
    const [users, posts, comments] = await Promise.all([
      fetch('/api/users').then(r => r.json()),
      fetch('/api/posts').then(r => r.json()),
      fetch('/api/comments').then(r => r.json()),
    ]);
    
    return { users, posts, comments };
  },
});

性能监控 #

使用 useEffect 监控重渲染 #

jsx
function MyComponent() {
  const value = useRecoilValue(myState);
  
  useEffect(() => {
    console.log('Component rendered:', Date.now());
  });
  
  return <div>{value}</div>;
}

使用 React DevTools Profiler #

jsx
<React.Profiler id="MyComponent" onRender={(id, phase, actualDuration) => {
  console.log(`${id} ${phase} took ${actualDuration}ms`);
}}>
  <MyComponent />
</React.Profiler>

常见性能陷阱 #

1. 在 Selector 中创建新对象 #

jsx
const badSelector = selector({
  key: 'bad',
  get: ({ get }) => {
    const items = get(itemsState);
    return { items };
  },
});

const goodSelector = selector({
  key: 'good',
  get: ({ get }) => {
    return get(itemsState);
  },
});

2. 过度使用 useRecoilState #

jsx
function BadComponent() {
  const [count, setCount] = useRecoilState(countState);
  
  return <button onClick={() => setCount(c => c + 1)}>+</button>;
}

function GoodComponent() {
  const setCount = useSetRecoilState(countState);
  
  return <button onClick={() => setCount(c => c + 1)}>+</button>;
}

3. 不必要的依赖 #

jsx
const badSelector = selector({
  key: 'bad',
  get: ({ get }) => {
    const allData = get(allDataState);
    return allData.filter(item => item.active);
  },
});

const goodSelector = selector({
  key: 'good',
  get: ({ get }) => {
    const activeIds = get(activeIdsState);
    return activeIds.map(id => get(itemFamily(id)));
  },
});

性能优化检查清单 #

  • [ ] 使用正确的 Hook(useRecoilValue、useSetRecoilState)
  • [ ] 拆分大对象为多个 Atom
  • [ ] 使用 atomFamily 管理列表项状态
  • [ ] 利用 Selector 缓存
  • [ ] 使用 React.memo 优化组件
  • [ ] 批量更新状态
  • [ ] 预取异步数据
  • [ ] 避免在 Selector 中创建新对象

总结 #

性能优化的核心要点:

优化方式 说明
选择正确 Hook 只读用 useRecoilValue,只写用 useSetRecoilState
状态拆分 拆分大对象为多个 Atom
Selector 缓存 利用自动缓存,配置缓存策略
组件优化 使用 React.memo,拆分组件
批量更新 使用 useRecoilCallback 批量更新

下一步,让我们学习 项目结构,了解 Recoil 项目的代码组织规范。

最后更新:2026-03-28