如何使用挂钩更新 SolidJS 中的本地存储值
How to update local storage values in SolidJS using hooks
我正在尝试为 solid-js 制作一个自定义“挂钩”,它将从本地存储中检索状态。
import { Accessor, createSignal, Setter } from "solid-js";
export default function createLocalStorageSignal<T extends string>(key: string): [get: Accessor<T>, set: Setter<T>] {
const storage = window.localStorage;
const initialValue: T = JSON.parse(storage.getItem(key) ?? '{}').value;
const [value,setValue] = createSignal<T>(initialValue);
const newSetValue: Setter<T> = (newValue) => {
setValue(newValue);
storage.setItem(key, JSON.stringify({value: newValue}));
return newValue;
}
return [
value,
newSetValue
]
}
但是我得到类型错误
Type '(newValue: any) => void' is not assignable to type 'Setter<T>'
为什么不能推断出newValue的类型?如果无法推断,我应该将其设置为什么?
编辑:
Setter<T>
的完整类型是
type Setter<T> = undefined extends T ?
<U extends T>
(v?: (U extends Function ? never : U) |
((prev?: T | undefined) => U) | undefined) => U :
<U extends T>
(v: (U extends Function ? never : U) |
((prev: T) => U)) => U
我不太了解 U
类型的用途及其工作原理。我认为问题与 newValue 可能是一个函数但 T 类型也可能是一个函数类型或其他东西有关...
这是一种方法:
import { Accessor, createSignal, Setter } from "solid-js";
export default function createLocalStorageSignal<T extends object>(
key: string
): T extends (...args: never) => unknown ? unknown : [get: Accessor<T>, set: Setter<T>];
export default function createLocalStorageSignal<T extends object>(key: string): [Accessor<T>, Setter<T>] {
const storage = window.localStorage;
const initialValue: T = JSON.parse(storage.getItem(key) ?? "{}").value;
const [value, setValue] = createSignal<T>(initialValue);
const newSetValue = (newValue: T | ((v: T) => T)): T => {
const _val: T = typeof newValue === 'function' ? newValue(value()) : newValue
setValue(_val as any);
storage.setItem(key, JSON.stringify({ value: _val }));
return _val;
};
return [value, newSetValue as Setter<T>];
}
type MyObjectType = {
foo: string
bar: number
}
const [get, set] = createLocalStorageSignal<MyObjectType>('asdf')
const val = get() // type of val is MyObjectType
set({} as MyObjectType) // ok
set(() => ({} as MyObjectType)) // ok
set((prev: MyObjectType) => ({} as MyObjectType)) // ok
const str: string = val.foo // ok
const num: number = val.bar // ok
const bool: boolean = val.foo // string is not assignable to boolean (as expected)
const sym: symbol = val.bar // number is not assignable to symbol (as expected)
// This is made to have a type error because function values can not be JSON.stringified.
const [get2, set2] = createLocalStorageSignal<() => void>('asdf')
const val2 = get2() // type of val is any, but that's because of the previous error.
与 React 不同,SolidJS 中的效果不限于组件,它们可以直接在函数中使用。
您可以在挂钩中创建信号,跟踪效果中的状态值,并在效果触发时更新本地存储。
try/catch 块是一种安全措施,以防无法解析先前存储的值。在这种情况下,它将被初始值覆盖。
import { createEffect } from 'solid-js';
import { render } from 'solid-js/web';
import { createStore, Store, SetStoreFunction } from 'solid-js/store';
function createLocalStore<T>(initState: T): [Store<T>, SetStoreFunction<T>] {
const [state, setState] = createStore(initState);
if (localStorage.mystore) {
try {
setState(JSON.parse(localStorage.mystore));
} catch (error) {
setState(() => initialState);
}
}
createEffect(() => {
localStorage.mystore = JSON.stringify(state);
});
return [state, setState];
}
const App = () => {
const [store, setStore] = createLocalStore({ count: 0 });
const handleClick = () => setStore('count', c => c + 1);
return (
<div onclick={handleClick}>count: {store.count}</div>
);
};
render(App, document.querySelector('#app'));
我正在尝试为 solid-js 制作一个自定义“挂钩”,它将从本地存储中检索状态。
import { Accessor, createSignal, Setter } from "solid-js";
export default function createLocalStorageSignal<T extends string>(key: string): [get: Accessor<T>, set: Setter<T>] {
const storage = window.localStorage;
const initialValue: T = JSON.parse(storage.getItem(key) ?? '{}').value;
const [value,setValue] = createSignal<T>(initialValue);
const newSetValue: Setter<T> = (newValue) => {
setValue(newValue);
storage.setItem(key, JSON.stringify({value: newValue}));
return newValue;
}
return [
value,
newSetValue
]
}
但是我得到类型错误
Type '(newValue: any) => void' is not assignable to type 'Setter<T>'
为什么不能推断出newValue的类型?如果无法推断,我应该将其设置为什么?
编辑:
Setter<T>
的完整类型是
type Setter<T> = undefined extends T ?
<U extends T>
(v?: (U extends Function ? never : U) |
((prev?: T | undefined) => U) | undefined) => U :
<U extends T>
(v: (U extends Function ? never : U) |
((prev: T) => U)) => U
我不太了解 U
类型的用途及其工作原理。我认为问题与 newValue 可能是一个函数但 T 类型也可能是一个函数类型或其他东西有关...
这是一种方法:
import { Accessor, createSignal, Setter } from "solid-js";
export default function createLocalStorageSignal<T extends object>(
key: string
): T extends (...args: never) => unknown ? unknown : [get: Accessor<T>, set: Setter<T>];
export default function createLocalStorageSignal<T extends object>(key: string): [Accessor<T>, Setter<T>] {
const storage = window.localStorage;
const initialValue: T = JSON.parse(storage.getItem(key) ?? "{}").value;
const [value, setValue] = createSignal<T>(initialValue);
const newSetValue = (newValue: T | ((v: T) => T)): T => {
const _val: T = typeof newValue === 'function' ? newValue(value()) : newValue
setValue(_val as any);
storage.setItem(key, JSON.stringify({ value: _val }));
return _val;
};
return [value, newSetValue as Setter<T>];
}
type MyObjectType = {
foo: string
bar: number
}
const [get, set] = createLocalStorageSignal<MyObjectType>('asdf')
const val = get() // type of val is MyObjectType
set({} as MyObjectType) // ok
set(() => ({} as MyObjectType)) // ok
set((prev: MyObjectType) => ({} as MyObjectType)) // ok
const str: string = val.foo // ok
const num: number = val.bar // ok
const bool: boolean = val.foo // string is not assignable to boolean (as expected)
const sym: symbol = val.bar // number is not assignable to symbol (as expected)
// This is made to have a type error because function values can not be JSON.stringified.
const [get2, set2] = createLocalStorageSignal<() => void>('asdf')
const val2 = get2() // type of val is any, but that's because of the previous error.
与 React 不同,SolidJS 中的效果不限于组件,它们可以直接在函数中使用。
您可以在挂钩中创建信号,跟踪效果中的状态值,并在效果触发时更新本地存储。
try/catch 块是一种安全措施,以防无法解析先前存储的值。在这种情况下,它将被初始值覆盖。
import { createEffect } from 'solid-js';
import { render } from 'solid-js/web';
import { createStore, Store, SetStoreFunction } from 'solid-js/store';
function createLocalStore<T>(initState: T): [Store<T>, SetStoreFunction<T>] {
const [state, setState] = createStore(initState);
if (localStorage.mystore) {
try {
setState(JSON.parse(localStorage.mystore));
} catch (error) {
setState(() => initialState);
}
}
createEffect(() => {
localStorage.mystore = JSON.stringify(state);
});
return [state, setState];
}
const App = () => {
const [store, setStore] = createLocalStore({ count: 0 });
const handleClick = () => setStore('count', c => c + 1);
return (
<div onclick={handleClick}>count: {store.count}</div>
);
};
render(App, document.querySelector('#app'));