zustand js -> ts

Jinmin Kim·2024년 10월 16일

폴더구조

zustand의 폴더구조는 아래와 같다.

1. handler.js
2. state.js
3. index.js

ts로 바뀔때는 그냥 전체 ts로 변경해주면된다.

1. handler.ts
2. state.ts
3. index.ts

index.ts

기존의 javascript

import create from "zustand";
import { devtools } from "zustand/middleware";


const store = create(
    devtools(
    (set, get) => ({
        ...State
        ,...Handler(set,get)
        })
    )
);

export default store;

변경의 typescript

import create from "zustand";
import {devtools} from "zustand/middleware";
import state from "@/services/store/zustand/state";
import handler from "@/services/store/zustand/handler";

const store =  create<{
    state: stateType;
    handler: handlerMethods;
}>(
    devtools(
        (set: any, get: any) => ({
            state: {
                ...state, // 상태 초기화
            },
            handler: {
                ...handler(
                    set,
                    () => get().state, // getState 함수를 전달
                    () => get().handler // getHandler 함수를 전달
                ),
            },
        })
    )
)

export default store;

state.ts

기존 javascript

const coverState = {
}
const createState =  {
    ...coverState
}

export default createState;

변경 typescript

export interface StateType {
    t: boolean
}

const coverState: StateType = {
    t: false
}

const createState =  {
    ...coverState
}

export default createState;

handler.ts

기존의 javascript

const handler = (set, get) => ({
    setName: (inputName) => {
        set({Name: inputName});
    },
});

export default handler;

변경된 typescript

typescript에서는

get: StateType | HandlerMethods

라고 해버리면,
getState()로 가져올때 어떤것이 state, handler인지 알수가없다.

그래서 state와 handler를 각각으로 나누어준다.

또한 index.ts에서

() => get().state, // getState 함수를 전달
() => get().handler // getHandler 함수를 

라고 하는것은 아래의 state, handler를 각각 사용하고자 함이다.

type SetState<T> = (partial: Partial<T> | ((state: T) => void)) => void;

type Handler = (set: SetState<any>, getState: () => StateType, getHandler: () => Handler) => HandlerMethods;

export interface HandlerMethods {
    setStore: (obj: any, callback?: any) => void;
}

const testHandler: Handler = (set,getState, getHandler) => ({
      employee: async () => {
        const {
            state,
            handler
        } = doubleStore.getState();

        const {
            test1
        } = state;

        const {
            setStore
        } = handler;
  //...
})

js -> ts로 변경하는것을 적용해보았다.

이후에 또 나타나는 이슈들에 대해서 추가할 예정이다.


재변경

index.ts

interface StateType {
    state: StateType;
    handler: HandlerMethods;
}

const comparsionStore = create<StateType>(
    devtools(
        (set, get) => ({
            state: {
                ...comparisonState, // 상태 초기화
            },
            handler: comparisonHandler(set, get),
        }),
    )
)

handler.ts

export interface HandlerMethods {
    setHighlighteBtn: (obj: any, callback?: any) => void;
    //...
}

const Handler = (set,get): HandlerMethods => ({
  	setHighlighteBtn: (flag) => {
        set({state: {...get().state, highlighteBtn: flag}});
    },

state.ts

//...변경 없음

재귀 함수 사용

zustand state에서 depth가 1~5,6 까지 갈수잇으니
재귀함수를 사용하여서 하나씩 아래와 같은 형식을 하나씩 만드는것이 아닌,

// 적용전
setTempData: (list) => {
  set((prev: any) => ({
    state: {
      ...prev.state,
      temp: {
        ...prev.state.temp,
        ...list, 
      },
    },
  }));
}

// 적용후
setTempData: (newData) => {
  updateStatePropertyAtPath(set, ['temp'], newData)
},

재귀함수를 사용하여 번거로움을 덜어낼수있을것같다.

재귀함수는 아래의 경우를 생각하고있다.

  1. 배열
  2. 객체
  3. 기본 자료형
// zustand state 변경을 위한 재귀 함수
const updateStatePropertyAtPath = <T extends object>(
    set: (fn: (prevState: any) => any) => void,
    path: Array<keyof T>,
    newData: Partial<any>
) => {
    set((prev: any) => {
        const updateAtPath = (obj: any, path: Array<keyof T>, value: Partial<any>) => {
            const [currentKey, ...restPath] = path;

            if (!restPath.length) {
                // 값이 객체인지 배열인지에 따라 다르게 처리
                if (Array.isArray(value)) {
                    // 배열일 경우 새로 대체
                    return {
                        ...obj,
                        [currentKey]: [...value],  // 배열 교체
                    };
                } else if (typeof value === 'object' && value !== null) {
                    // 객체일 경우 부분 병합
                    return {
                        ...obj,
                        [currentKey]: {
                            ...obj[currentKey],
                            ...value,  // 객체 병합
                        },
                    };
                } else {
                    // 기본 자료형일 경우 그대로 대체
                    return {
                        ...obj,
                        [currentKey]: value,  // 기본 값 교체
                    };
                }
            }

            // 아직 경로가 남아있다면 계속 재귀적으로 처리
            return {
                ...obj,
                [currentKey]: updateAtPath(obj[currentKey], restPath, value),
            };
        };

        return {
            state: updateAtPath(prev.state, path, newData),
        };
    });
};


export {updateStatePropertyAtPath}

위의 재귀함수를 기억해두면 다른곳에서도 충분히 사용할수잇을것 같다.

profile
Let's do it developer

0개의 댓글