useRecoilState #

什么是 useRecoilState? #

useRecoilState 是 Recoil 提供的核心 Hook,类似于 React 的 useState,但用于读写 Recoil 状态。它返回一个数组:[value, setValue]

jsx
const [value, setValue] = useRecoilState(state);

基本用法 #

读取和更新状态 #

jsx
import { atom, useRecoilState } from 'recoil';

const countState = atom({
  key: 'countState',
  default: 0,
});

function Counter() {
  const [count, setCount] = useRecoilState(countState);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

与 useState 的对比 #

jsx
function CounterWithUseState() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

function CounterWithRecoil() {
  const [count, setCount] = useRecoilState(countState);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

主要区别:

特性 useState useRecoilState
作用域 组件内部 全局
状态共享 需要手动传递 自动共享
持久化 组件卸载后丢失 可配置持久化
调试 React DevTools Recoil DevTools

更新状态的方式 #

直接设置值 #

jsx
const [count, setCount] = useRecoilState(countState);

setCount(10);
setCount(0);

使用函数更新 #

jsx
setCount(prev => prev + 1);
setCount(prev => prev * 2);
setCount(prev => Math.max(0, prev - 1));

条件更新 #

jsx
const increment = () => {
  setCount(prev => {
    if (prev >= 10) {
      console.log('Max reached');
      return prev;
    }
    return prev + 1;
  });
};

更新对象状态 #

替换整个对象 #

jsx
const [user, setUser] = useRecoilState(userState);

setUser({
  id: 1,
  name: 'John',
  email: 'john@example.com',
});

更新部分属性 #

jsx
setUser(prev => ({
  ...prev,
  name: 'Jane',
}));

setUser(prev => ({
  ...prev,
  email: 'jane@example.com',
}));

嵌套对象更新 #

jsx
const [settings, setSettings] = useRecoilState(settingsState);

setSettings(prev => ({
  ...prev,
  theme: {
    ...prev.theme,
    primaryColor: '#007bff',
  },
}));

更新数组状态 #

添加元素 #

jsx
const [todos, setTodos] = useRecoilState(todoListState);

setTodos(prev => [...prev, newTodo]);
setTodos(prev => [newTodo, ...prev]);

删除元素 #

jsx
setTodos(prev => prev.filter(todo => todo.id !== id));

更新元素 #

jsx
setTodos(prev => prev.map(todo =>
  todo.id === id ? { ...todo, completed: true } : todo
));

排序 #

jsx
setTodos(prev => [...prev].sort((a, b) => a.priority - b.priority));

使用 Selector #

useRecoilState 也可以用于可写 Selector:

jsx
import { atom, selector, useRecoilState } from 'recoil';

const tempCelsiusState = atom({
  key: 'tempCelsius',
  default: 25,
});

const tempFahrenheitState = selector({
  key: 'tempFahrenheit',
  get: ({ get }) => get(tempCelsiusState) * 9 / 5 + 32,
  set: ({ set }, value) => set(tempCelsiusState, (value - 32) * 5 / 9),
});

function TemperatureInput() {
  const [fahrenheit, setFahrenheit] = useRecoilState(tempFahrenheitState);
  
  return (
    <input
      type="number"
      value={fahrenheit}
      onChange={(e) => setFahrenheit(Number(e.target.value))}
    />
  );
}

性能优化 #

避免不必要的重渲染 #

如果只需要读取或写入,使用专门的 Hook:

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

function DisplayOnly() {
  const count = useRecoilValue(countState);
  return <div>{count}</div>;
}

使用函数更新避免依赖 #

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

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

实战示例:表单处理 #

jsx
import { atom, useRecoilState } from 'recoil';

const formState = atom({
  key: 'formState',
  default: {
    username: '',
    email: '',
    password: '',
    confirmPassword: '',
  },
});

function Form() {
  const [form, setForm] = useRecoilState(formState);
  
  const handleChange = (field) => (e) => {
    setForm(prev => ({
      ...prev,
      [field]: e.target.value,
    }));
  };
  
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('Submit:', form);
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        value={form.username}
        onChange={handleChange('username')}
        placeholder="Username"
      />
      <input
        type="email"
        value={form.email}
        onChange={handleChange('email')}
        placeholder="Email"
      />
      <input
        type="password"
        value={form.password}
        onChange={handleChange('password')}
        placeholder="Password"
      />
      <input
        type="password"
        value={form.confirmPassword}
        onChange={handleChange('confirmPassword')}
        placeholder="Confirm Password"
      />
      <button type="submit">Submit</button>
    </form>
  );
}

实战示例:购物车 #

jsx
import { atom, useRecoilState } from 'recoil';

const cartState = atom({
  key: 'cartState',
  default: [],
});

function useCart() {
  const [cart, setCart] = useRecoilState(cartState);
  
  const addItem = (item) => {
    setCart(prev => {
      const existing = prev.find(i => i.id === item.id);
      if (existing) {
        return prev.map(i =>
          i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i
        );
      }
      return [...prev, { ...item, quantity: 1 }];
    });
  };
  
  const removeItem = (itemId) => {
    setCart(prev => prev.filter(i => i.id !== itemId));
  };
  
  const updateQuantity = (itemId, quantity) => {
    if (quantity <= 0) {
      removeItem(itemId);
      return;
    }
    setCart(prev => prev.map(i =>
      i.id === itemId ? { ...i, quantity } : i
    ));
  };
  
  const clearCart = () => {
    setCart([]);
  };
  
  const total = cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
  
  return {
    cart,
    addItem,
    removeItem,
    updateQuantity,
    clearCart,
    total,
  };
}

function ShoppingCart() {
  const { cart, updateQuantity, removeItem, total } = useCart();
  
  return (
    <div>
      {cart.map(item => (
        <div key={item.id}>
          <span>{item.name}</span>
          <input
            type="number"
            value={item.quantity}
            onChange={(e) => updateQuantity(item.id, Number(e.target.value))}
          />
          <button onClick={() => removeItem(item.id)}>Remove</button>
        </div>
      ))}
      <div>Total: ${total}</div>
    </div>
  );
}

TypeScript 支持 #

tsx
import { atom, useRecoilState } from 'recoil';

interface User {
  id: number;
  name: string;
  email: string;
}

const userState = atom<User | null>({
  key: 'userState',
  default: null,
});

function UserProfile() {
  const [user, setUser] = useRecoilState(userState);
  
  const updateName = (name: string) => {
    if (user) {
      setUser({ ...user, name });
    }
  };
  
  if (!user) return <div>Not logged in</div>;
  
  return (
    <div>
      <input
        value={user.name}
        onChange={(e) => updateName(e.target.value)}
      />
    </div>
  );
}

总结 #

useRecoilState 的核心要点:

用法 说明
[value, setValue] 返回值和设置函数
直接设置 setValue(newValue)
函数更新 setValue(prev => newValue)
对象更新 使用展开运算符
数组更新 使用 map/filter/spread

下一步,让我们学习 useRecoilValue,了解只读状态的 Hook。

最后更新:2026-03-28