reactjs - פונקציית מצב ב-zustand לשינוי תכונה אחת של אובייקט


אז אני משתמש ב-Zustand עם React באפליקציית ה-Electron שלי. אני משתמש בזה כדי לשלוף קצת נתונים ממסד הנתונים Dexie. זה רק ערך אחד והוא נשמר ל-state. אני מנסה לקבל את פונקציית set מהרכיב המוצג כדי להיות מסוגל לשנות את ה-state של התכונה המתאימה שלו ב-state. ב-React useState הקוד הזה עובד...

setMyState({...appSettings, [val]: data})

אבל ב-zustand המצב שלי נראה ככה והקוד הזה לא עובד

setAppSettings: (data: unknown, val: string) => {
        set((state) => ({...state.appSettings, [val]: data}))
}

Webstorm אומר לי "Argument of type is not assignable to parameter of type". שיניתי את סוג ה"data" ל-any וזה לא משפיע. כאשר אני מריץ את הקוד הזה ולוחץ על הכפתור לשינוי, שום דבר לא קורה, לא אפילו שגיאה. שיניתי את זה לקוד הזה עם אותו התוצאה, שום דבר לא קורה.

setAppSettings: (data: any, val: string) => {
        set((state) => ({
            appSettings: {...state.appSettings, [val]: data}
        }))
},

עם הקוד השני הזה אני לא מקבל שגיאות ב-Webstorm. אז לא ברור מה קורה כי הקונסולה לא נותנת לי כלום. כמו כן, הנראה שהסטייט לא מתעדכן. אני מוסיף כפתור שמדפיס בקונסולה את כל האובייקט 'appSettings' והוא נשאר ללא שינוי.

אני מרגיש שזהו פתרון פשוט אבל אני מתמודד עם זה כבר הרבה זמן כדי להבין את זה... יש למישהו תובנה על זה?

------עריכה לתשובה הראשונה אני לא כל כך בטוח שהסוג של הנתונים הוא הבעיה? אני חושב שזה יותר שאני לא כל כך מכיר איך זוסטנד עובד? אבל הפונקציה שמנסה לשמור את המצב היא...

// store.ts
type AppSettings = {
  appSettings: Settings;
  setAppSettings: (role: string, data: unknown) => void;
  getAppSettings: () => void;
}
export const useAppSettings = create<AppSettings>((set) => ({
  appSettings: {
      id: 0,
      sortByTime: false,
      sortByFirstName: false,
      colorMode: '',
      hours: 24,
  },
  setAppSettings: (role: string, data: unknown) => {
      set((state) => ({
          appSettings: {...state.appSettings, [role]: data}
      }))
  },
  getAppSettings: async () => {
      const result = await db.settings.toCollection().first();
      if (result === undefined) {
          console.log("No settings in db, using default")
      } else {
          set(() => ({appSettings: result}))
      }
  },
}))
//Interface for settings object - in another file
export interface Settings {
  id: number,
  sortByTime: boolean,
  sortByFirstName: boolean,
  colorMode: string,
  hours: number,
}

// This buttonCreate function is used to duplicate 4 buttons that 
// all look alike and have the same operation. 

const setSettings = useAppSettings((state) => state.setAppSettings)

const buttonCreate = (forRole: string, con1: any, con2: any) => {
  return (
    <ToggleButtonGroup type="radio" name={forRole} className="mb-2 warning" value={settings[forRole]}>
       <ToggleButton id={forRole + "-toggle-1"} value={con1}
                     className={"btn-dark"} onClick={() => setSettings(forRole, con1)}>
                    {typeof con1 === "boolean" ? "Yes" : con1}
       </ToggleButton>
       <ToggleButton id={forRole + "-toggle-2"} value={con2}
                     className={"btn-dark"} onClick={() => setSettings(forRole, con2)}>
                    {typeof con2 === "boolean" ? "No" : con2}
       </ToggleButton>
    </ToggleButtonGroup>
  )
}

תשובה #1

אז במקרה הראשון:

setAppSettings: (data: unknown, val: string) => {
        set((state) => ({...state.appSettings, [val]: data}))
}

בעצם מה שקורה הוא שהמצב שלך יש לו סוג type כמו RootState, AppState או משהו כזה.

ו

Webstorm is telling me "Argument of type is not assignable to parameter of type". 

מה זה אומר, שכאשר יש לך setter כמו [key] ב-TypeScript, TypeScript לא יכול להמיר את [key:string]: unknown לסוג של ה-state שלך, ולכן הוא זורק לך Argument of type is not assignable

כדי למנוע זאת, אתה יכול להוסיף as any אבל לא ל-data, אלא לתוצאה של הפונקציה

setAppSettings: (data: unknown, val: string) => {
        set((state) => ({...state.appSettings, [val]: data} as any))
}

בעצם, האובייקט שמוחזר על ידי (state) => צריך להיות typeof (state)

יש דרכים לעקוף, כמו שימוש ב-as const, enums, typeof keyof, אבל צריך יותר מהקוד שלך כדי לתת תשובה נכונה...

עדכון

הנה הטעות שלך:

export type AppSettings = {
  appSettings: Settings;
  setAppSettings: (data: Settings, val: string) => void; // << data: Settings 
  getAppSettings: () => void;
};

// and then you override type:
  setAppSettings: (data: unknown, val: string) => ...

והנה קוד שעובד:

import { create } from "zustand";
import createSelectors from "./selectors";

export type AppSettings = {
  appSettings: Settings;
  setAppSettings: (data: unknown, val: string) => void;
  getAppSettings: () => void;
};

interface Settings {
  id: number;
  sortByTime: boolean;
  sortByFirstName: boolean;
  colorMode: string;
  hours: number;
}

const appSettings = create<AppSettings>()((set) => ({
  appSettings: {
    id: 1,
    sortByTime: false,
    sortByFirstName: false,
    colorMode: "",
    hours: 0,
  },
  // u also had error here: 
  setAppSettings: (data, val) =>
    set((state) => ({
      ...state, // u need pass state here... 
      appSettings: { ...state.appSettings, [val]: data },
    })),
  getAppSettings: () => {},
}));

export const useAppSettings = createSelectors(appSettings);

תוקן עוד שגיאה אחת.