zustand js -> ts

Jinmin Kim·2024년 10월 16일
0

폴더구조

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개의 댓글

관련 채용 정보