你可能根本不需要useEffect这个钩子
useEffect主要用于当你想要同步React和第三方系统的时候,比如与Leaflet库同步,或者与localStorage、DOM同步。
用useEffect映射数据结果
有时我们会倾向于用useEffect来映射数据,但其实完全没有必要。下面是一个例子:
// 不需要这样做
useEffect(() => {
setMappedResult(resultFromFetch.map((p) => p.name));
}, [resultFromFetch]);
// 可以直接这样写:
const mappedResult = resultFromFetch.map((p) => p.name);
这更简单直接。为了性能,也可以用useMemo:
const mappedResult = useMemo(() => resultFromFetch.map((p) => p.name), [resultFromFetch]);
用useEffect合并state或props
下面是一个反例:
// 不需要这样
useEffect(() => {
setFullName(`${firstName} ${lastName}`);
}, [firstName, lastName]);
// 直接合并展示:
const fullName = `${firstName} ${lastName}` ;
重置组件的全部state
有时需要重置一个组件的全部state,这时不应该使用useEffect,而是使用key属性强制重新渲染:
export default function App() {
const [key, setKey] = useState(Math.random());
const handleReset = () => {
setKey(Math.random());
};
return (
<div className="App">
<StatefulComponent key={key} />
<button type="button" onClick={handleReset}>
Reset
</button>
</div>
);
}
处理用户事件
用户事件比如点击、改变、输入等应该直接绑定到元素上,不需要通过useEffect。
订阅存储
对于需要订阅的外部存储,React提供了useSyncExternalStore钩子:
const subscribe = (callback) => {
window.addEventListener("storage", callback);
return () => {
window.removeEventListener("storage", callback);
};
};
const getSnapShot = () => {
return localStorage.getItem("lang");
};
export default function App() {
const language = useSyncExternalStore(subscribe, getSnapShot);
const handleLanguage = () => {
const newLang = language === "english" ? "spanish" : "english";
localStorage.setItem("lang", newLang);
dispatchEvent(new StorageEvent("storage", { key: "lang", newLang }));
};
return (
<div className="App">
<div>
<span>Value: </span>
{language}
</div>
<button type="button" onClick={handleLanguage}>
Change Value
</button>
</div>
);
}
它可以方便地分离订阅和获取更新值的逻辑。