反冲有没有办法在原子状态改变时做额外的动作?

recoil is there a way to do additional actions when the atom state changes?

我将主题状态作为后坐力的原子来管理。

如果用户通过ui改变了主题,相应的原子状态也会改变。

我想将这里更改的主题值保存到localstorage。 为此,我们需要知道该状态何时发生变化。

当然可以在调用相应的atom状态变化之前加上localstorage存储代码,但是如果atom用在很多地方,效率就太低了

此时我应该使用选择器吗?但是,由于主题是单一状态,因此ui将其作为派生状态来扩展状态更改逻辑并不明智。

如果您知道,我们将不胜感激。

example code

import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { RecoilRoot, atom, useRecoilState } from "recoil";

type ThemeItem = "light" | "dark";
// : I want to do some extra work when this state changes (ex: Save the changed state to localStorage)
const themeState = atom<ThemeItem>({
  key: "themeState",
  default: "light",
});

function App() {
  const [theme, setTheme] = useRecoilState(themeState);

  function handleTheme(theme: ThemeItem) {
    return () => setTheme(theme);
  }

  return (
    <>
      <h3>{theme}</h3>
      <button onClick={handleTheme("light")}>light</button>
      <button onClick={handleTheme("dark")}>dark</button>
    </>
  );
}

ReactDOM.render(
  <StrictMode>
    <RecoilRoot>
      <App />
    </RecoilRoot>
  </StrictMode>,
  document.getElementById("root")
);

我之前解决这个问题的方法是实现一个 ThemeProvider(或者你想给它起的任何名字),然后使用 useEffect 从 [=13= 存储和加载值]:

import { StrictMode, useEffect } from "react";
import ReactDOM from "react-dom";
import { RecoilRoot, atom, useRecoilState } from "recoil";

type ThemeItem = "light" | "dark";
// : I want to do some extra work when this state changes (ex: Save the changed state to localStorage)
const themeState = atom<ThemeItem>({
  key: "themeState",
  default: "light",
});

function ThemeProvider() {
  const [theme, setTheme] = useRecoilState(themeState);

  useEffect(() => {
    // Save theme in localStorage on change
    localStorage.setItem('theme', theme);
  }, [theme]);

  useEffect(() => {
    // Load theme from localStorage on mount
    if (!localStorage.getItem('theme')) return;
    setTheme(localStorage.getItem('theme') as ThemeItem);
  }, []);
}

function App() {
  const [theme, setTheme] = useRecoilState(themeState);

  function handleTheme(theme: ThemeItem) {
    return () => setTheme(theme);
  }

  return (
    <>
      <h3>{theme}</h3>
      <button onClick={handleTheme("light")}>light</button>
      <button onClick={handleTheme("dark")}>dark</button>
    </>
  );
}

ReactDOM.render(
  <StrictMode>
    <RecoilRoot>
      <ThemeProvider />
      <App />
    </RecoilRoot>
  </StrictMode>,
  document.getElementById("root")
);