【问题标题】:How to update local storage values in SolidJS using hooks如何使用钩子更新 SolidJS 中的本地存储值
【发布时间】:2022-07-26 21:07:23
【问题描述】:

我正在尝试为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&lt;T&gt;的完整类型是

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


    【解决方案1】:

    这是一种方法:

    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 playground example

    【讨论】:

    • 不错!我基于你的mine
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      trusktr's answer 的基础上,简化setValue 函数并为defaultValuestorage 添加参数(因此您可以根据需要使用sessionStorage):

      export default function createStoredSignal<T>(key: string, defaultValue: T, storage = localStorage): Signal<T> {
        const initialValue = storage.hasItem(key) 
          ? JSON.parse(storage.getItem(key)) as T 
          : defaultValue;
      
        const [value, setValue] = createSignal<T>(initialValue);
      
        // TS kvetches, but we're passing arg to setValue and returning what it returns, 
        // so this could not be more correctly typed
        // @ts-ignore
        const setValueAndStore: typeof setValue = (arg) => {
          const v = setValue(arg);
          storage.setItem(key, JSON.stringify(v));
          return v;
        };
      
        return [value, setValueAndStore];
      }
      
      

      【讨论】:

        猜你喜欢
        • 2018-02-18
        • 2014-03-22
        • 1970-01-01
        • 2020-10-27
        • 2020-07-07
        • 2021-05-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多