주스탄드에서 getter 사용 가능

쑹춘·2023년 4월 26일
0

주스탄드와 게터

목록 보기
1/3

그렇게 하도록 middleware를 작성, 이름은 zustand-stateful-getter


저기에 적어놓은 것처럼, 주스탄드는 초기 상태 객체의 getter를 기대한 것처럼 다루지 않고 getter를 일반 primitive value를 지닌 속성으로 변경한다. setState API는 전달받은 partial state를 Object.assign으로 재할당하기 때문. Object.assign API를 가지고 얕은 복사가 된 새로운 객체를 만든다.

o = { a: 1, get b() { return this.a; } }; // { a: 1, b: [Getter] }
o.b; // 1

o = Object.assign({}, o, { a: 2 }); // { a: 2, b: 1 }

만약 얕은 복사를 하지 않았다면 초기 상태의 getter는 살아남을 수 있는데,

o = { a: 1, get b() { return this.a; } };
o.b; // 1
o = Object.assign(o, { a: 2 }); // { a: 3, b: [Getter] }
o.b; // 2

다만 이렇게 한다면, 상태 객체의 속성 값은 변경을 하였어도 객체 포인터는 같기 때문에 useEffect와 같은 의존성 기반 diff가 의도한대로 동작하지 않을 것이다. 이것은 조금 짜증이 날 수 있겠는데 react 개발자라면 알아서 조심할 수 밖에 없는 일이다. 아무튼,


react에서 useEffect, useMemo가 고장나지 않으면서 getter가 의존하는 속성의 값이 변경될 때 getter도 변경되었다고 react 부작용 콜백을 적절하게 호출할 것.

getter의 의존성을 어떻게 파악할까?

원랜 zustand-stateful-getter의 서브 프로시저로 작성을 하였다가, 이것을 별도 라이브러리로 추출해두면 나중에도 도움이 될 거라는 계시를(어디서?) 받아서 detective-getter-deps를 따로 만들었다.

import { detectGetterDeps } from 'detective-getter-deps';

const dependencies = detectGetterDeps({
  firstName: 'jeans',
  lastName: 'new',
  get fullName() {
    return [this.firstName, this.lastName].join(' ');
  },
});

dependencies.get('fullName'); // Set { 'firstName', 'lastName' }

getter가 있는 객체를 넣으면, 우선 getter 속성을 추출하고, getter들이 사용 중인 의존 속성 목록을 반환한다. 이제 처음 createStore를 호출할 때에 getter의 목록과 각각의 의존성을 파악하였으니, setState가 호출이 될 때마다 getter의 setState를 호출하자. 코드는 여기쯤에 있다.

참, 실제론 클라이언트가 setState를 호출할 때에 getter를 넣는 게 아니라, getter의 피의존성dependants인 속성을 입력할 테니 한 번 뒤집어 두는 게 좋겠다. (*이 동작 detectGetterDeps에 있는 게 맞는가? 나의 오랜 걱정 고민이다.)

const getterDeps = detectGetterDeps(initialStore);
const propertyDependants = inverseDeps(getterDeps);

type Deps<T> = Map<keyof T, Set<keyof T>>;

export const inverseDeps = <T>(deps: Deps<T>) =>
  Array.from(deps.entries()).reduce<Deps<T>>(
    (accumulator, [property, deps]) => {
      deps.forEach((dep) => {
        accumulator.set(dep, (accumulator.get(dep) || new Set()).add(property))
      });

      return accumulator;
    },
    new Map()
  );

고치고 싶은 것들

각 getter의 의존성은 스토어가 만들어지는 최초에만 한 번 파악을 하고, 그것을 저장하여 나중에 사용한다. 만약 getter 메소드 바디 내부에 조건 구문이 있고, 초기 상태일 때에는 특정 블록에 접근하지 않으며 그 블록에만 있는 객체 의존성이 있다면 형사는 getter deps를 파악할 수 없다.

  1. 미들웨어 2번째 매개변수로 옵션을 받아서, setState가 호출이 될 때마다 매번 getter의 의존성을 재계산하는 방법과
  2. 혹은 클라이언트 쪽에서 명시적으로 getter deps를 작성하도록 하는 방법

이렇게 두 가지가 있을 것이다. 첫번째는 다소 낭비적일 수도 있겠는데, 어쩌면 괜찮을 수도 있겠고

그래서 두번째 할 일은 이 미들웨어의 성능 분석이다.

더 보기

내가 필요해서 만들었는데 이제 홍보는 어떻게 하지? 목표는 주스탄드의 third-party-libraries.md에 등재되는 것이 목표이다.

profile
성천

0개의 댓글