如何使用钩子更新SolidJS中的本地存储值

问题描述 投票:0回答:3

我正在尝试为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 类型也可能是一个函数类型或其他东西有关......

typescript local-storage solid-js
3个回答
6
投票

trusktr 的答案的基础上,简化

setValue
包装函数并添加
defaultValue
storage
的参数(这样你就可以使用
sessionStorage
):

function createStoredSignal<T>(
   key: string, 
   defaultValue: T, 
   storage = localStorage
): Signal<T> {

  const initialValue = storage.getItem(key) 
    ? JSON.parse(storage.getItem(key)) as T 
    : defaultValue;

  const [value, setValue] = createSignal<T>(initialValue);

  const setValueAndStore = ((arg) => {
    const v = setValue(arg);
    storage.setItem(key, JSON.stringify(v));
    return v;
  }) as typeof setValue;

  return [value, setValueAndStore];
}


5
投票

与 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 extends object>(
  initState: T
): [Store<T>, SetStoreFunction<T>] {
  const [state, setState] = createStore(initState);

  if (localStorage.mystore) {
    try {
      setState(JSON.parse(localStorage.mystore));
    } catch (error) {
      setState(() => initState);
    }
  }

  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'));

如果需要使用自定义键来存储变量:

import { createEffect } from 'solid-js';
import { render } from 'solid-js/web';
import { createStore, Store, SetStoreFunction } from 'solid-js/store';

function createLocalStore<T extends object>(
  initState: T, key: string
): [Store<T>, SetStoreFunction<T>] {
  const [state, setState] = createStore<T>(initState);

  if (localStorage[key]) {
    try {
      setState(JSON.parse(localStorage[key]));
    } catch (error) {
      setState(() => initState);
    }
  }

  createEffect(() => {
    localStorage[key] = JSON.stringify(state);
  });

  return [state, setState];
}


export const App = () => {
  const [store, setStore] = createLocalStore({ count: 0 }, 'my-store');

  const handleClick = () => setStore('count', c => c + 1);

  return (
    <div onclick={handleClick}>count: {store.count}</div>
  );
};

3
投票

这是一种方法:

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.

TS 游乐场示例

© www.soinside.com 2019 - 2024. All rights reserved.