useLocalStorageState types: Return `T` state instead of `T | undefined` when `defaultValue` is set
Currentlym when you pass useLocalStorageState a { defaultValue: "abc" }, the returned "getter" is still of type T | undefined instead of just T:
const [myVal, setMyVal] = useLocalStorageState("my-key", { defaultValue: "abc" });
This is annoying, because it means you cannot use useLocalStorageState()/useSessionStorageState() as a drop-in replacement for React's useState() -- you always have to handle the undefined manually.
But logically, it seems the TypeScript typings are just wrong / less precise than they could be, and if defaultValue is set myVal should just be of type T, is that right?
If yes, could the typings be fixed?
It looks like this was already fixed multiple times in the past:
- #967
- https://github.com/alibaba/hooks/pull/2322#issuecomment-1716190344
- This latest comment suggests that
undefinedis possible becausesetState(undefined)is allowed to clear the value.
- This latest comment suggests that
So it looks like it's not possible with the current API, but would be possible if another option was added that forbids passing undefined.
Maybe thats desirable to allow an API-compatible replacement of useState(), but maybe the user should just write 2 lines more code to handle the default outside, or write a wrapper function.
Here is my wrapper function example:
import { useLocalStorageState } from "ahooks";
import * as UseLocalStorageState from "ahooks/es/createUseStorageState"
// Wraps https://ahooks.js.org/hooks/use-local-storage-state/
// but using our error-as-warning logging, and enforcing a default value so that
// the return type is the same as that of `React.useState()`.
export function useLocalStorageStateWithDefault<T>(
key: string,
// Enforce that `defaultValue` must be given, so that return value of `state` is `T` instead of `T | undefined`.
// See also https://github.com/alibaba/hooks/issues/2773 gets implemented.
// To make sure that one cannot call `setState(undefined)` to clear the storage
// (in which case the `state` would become `T | undefined` again),
// we also use
// value: UseLocalStorageState.SetState<T>
// in the return type below, and not the default
// value?: UseLocalStorageState.SetState<T>
options: UseLocalStorageState.Options<T> & { defaultValue: T | (() => T) },
): readonly [T, (value: UseLocalStorageState.SetState<T>) => void] {
const [state, setState] = useLocalStorageState<T>(key, options);
if (state === undefined) {
throw new Error("BUG: useLocalStorageStateWithDefault: Got undefined even though defaultValue was set");
}
return [state, setState];
}
Example usage:
const [myVal, setMyVal] = useLocalStorageStateWithDefault("my-key", { defaultValue: "abc" });
// now `myVal: T`, and `setMyVal(undefined)` is rejected
- #2795 this ?
Hello @nh2. Please provide a online reproduction by forking this link https://codesandbox.io/s/ok2fe or a minimal GitHub repository. Issues labeled by Need Reproduce will be closed if no activities in 3 days.
你好 @nh2, 我们需要你提供一个在线的重现实例以便于我们帮你排查问题。你可以通过点击 此处 创建一个 codesandbox 或者提供一个最小化的 GitHub 仓库。3 天内未跟进此 issue 将会被自动关闭。
