본문 바로가기

FrontEnd

Recoil Pattern : Selector Composition (셀렉터 조합하기)

반응형

https://learnrecoil.com/video/selector-composition

 

Selector Composition - Free Recoil.js Course

We create a new selectorFamily that composes functionality on top of our existing editProperty selectorFamily.

learnrecoil.com

기존 editProperty selectorFamily 위에 기능을 구성하는 새로운 selectorFamily를 만듭니다.

Motivation

리코일은 각 아톰, 셀렉터의 getter, setter를 조합해 상위 getter setter를 조합하여 파생 상태를 만든다.

(selector는 반드시 아톰으로부터 파생할 필요는 없다.)

반드시 두 개 이상의 atom / selector로부터 파생할 필요도 없고, 단순히 view 혹은 set 로직을 위해 selector를 확장하는 것도 가능하다.

selector- 다른 셀렉터/아톰의 데이터 + 로직

즉, 메서드 오버라이딩, 같은 개념으로 생각해도 되는 것이다.

(주 : Recoil은 실제로 함수형 프로그래밍의 Lens 아이디어를 차용한 API이기도 하다.)

https://itchallenger.tistory.com/500?category=975123 

 

먼저 path, id, newValue를 이용한 selector가 있다 가정하자.

해당 컴포넌트는 input으로 들어오는 값을 통해 동작한다.

export const editProperty = selectorFamily<any, {path: string; id: number}>({
    key: 'editProperty',

    get: ({path, id}) => ({get}) => {
        const element = get(elementState(id))
        return _.get(element, path)
    },

    set: ({path, id}) => ({get, set}, newValue) => {
        const element = get(elementState(id))
        const newElement = produce(element, (draft) => {
            _.set(draft, path, newValue)
        })
        set(elementState(id), newElement)
    },
})

// 이렇게 사용
set(editProperty({path: `style.size.${dimension}`, id}), newValue)

이번엔 해당 값을 드래그를 통해서도 수정할 수 있도록 하고 싶다.

위의 셀렉터의 getter와 setter를 사용하면 양방향 동기화가 된다.

(get에 의해 직전 방법대로 수정 시, set에 의해 신규 방법으로 수정 시)

const editSize = selectorFamily<any, {dimension: 'width' | 'height'; id: number}>({
    key: 'editSize',

    get: ({dimension, id}) => ({get}) => {
        return get(editProperty({path: `style.size.${dimension}`, id}))
    },

    set: ({dimension, id}) => ({get, set}, newValue) => {
        const hasImage = get(editProperty({path: 'image', id})) !== undefined
        if (!hasImage) {
            set(editProperty({path: `style.size.${dimension}`, id}), newValue)
            return
        }

        const {width, height} = get(editProperty({path: 'style.size', id}))
        const aspectRatio = width / height

        if (dimension === 'width') {
            set(editProperty({path: 'style.size', id}), {
                width: newValue,
                height: Math.round(newValue / aspectRatio),
            })
        } else {
            set(editProperty({path: 'style.size', id}), {
                height: newValue,
                width: Math.round(newValue * aspectRatio),
            })
        }
    },
})

 

 

 

반응형