티스토리 뷰

이 글은 공식문서를 보고 요약, 의역 정리한것입니다.

observable은 mobx패키지에서

// wrong
import { observable } from "mobx/lib/mobx"
// correct
import { observable } from "mobx"

Use @observer on all components that render @observables.

observable을 사용하는 모든 컴포넌트에 observer를 달아라. 그래야 mobx가 최대한 최적화를 해줄 수 있다.

observer는 inject보다 먼저

// wrong
@observer
@inject('store')
// correct
@inject('store')
@observer

observable을 받아서 컴포넌트 내부에 한번 더 저장하지 마라.

잘못된 케이스

class User {
    @observable name
}

class Profile extends React.Component {
    name

    componentWillMount() {        
    	// 1. 이렇게 observable을 내부로 한번더 참조하면 안된다.
        this.name = this.props.user.name
    }

    render() {
        return <div>{this.name}</div>
    }
}

mobx는 참조 관계를 관찰한다. this.props.user.name 변수는 어떤 string을 가리키는 화살표일텐데 이 화살표(참조관계) 자체를 관찰하는것이다. 그래서 이 객체를 다른 변수(this.name)로 가리키게 한다고 해서 mobxthis.name에 의한 변경사항을 감시할 수 있는것이 아니다.

 

올바른 케이스

class User {
    @observable name
}

class Profile extends React.Component {
    @computed get name() {
    	// this.props.user.name이 변경되면 name값이 재계산된다.
        // 재계산된 computed name을 render에서 사용하므로
        // 정상 동작한다.
        return this.props.user.name
    }

    render() {
        return <div>{this.name}</div>
    }
}

computed는 내부에 있는 observable state가 변경되면 알아서 재계산 및 캐싱된다. 또한 이 name이라고 하는 computed는 render메소드 내부에서 사용되고 있다. 따라서, 리렌더링이 일어난다.

 

Render Callback Pattern은 Mobx가 관리하지 못한다.

컴포넌트를 합치는 방식중에 Children을 받아서 합치는 방식이 있고 render props에 컴포넌트를 받아서 합치는 방식이 있다.

Mobx의 Observer는 컴포넌트의 render()에만 적용된다.

따라서 컴포넌트를 render props로 받아서 렌더링하면 그 받아온 컴포넌트는 Mobx의 관리대상에서 제외된다.

 

render props: 부모 컴포넌트에서 자식 컴포넌트를 render라고 하는 props로 받아서 렌더링 하는 방식
render function: 컴포넌트 자기 자신의 JSX가 담긴 부분.

 

역참조는 최대한 늦게 해라.

역참조란?
객체의 property를 참조하는 것이다.
예를들어, example이라는 객체의 hello라는 property는 example.hello와 같이 역참조(dereference)될 수 있다.

위에서 Mobx는 화살표(참조 관계)를 감시한다고 했다.

ReactDOM.render(<Timer timerData={timerData.secondsPassed} />, document.body)

만약에 timerData.secondsPassed(Observable State)를 이런식으로 전달하면 리렌더링 되지 않는다.

왜냐면, timerData.secondsPassed는 값이기 때문이다. 이 값이 0이라면 그냥 컴포넌트에 0이라는 을 전달한거지 observable state를 전달한게 아니다.

 

Mobx는 javascript의 primitive value(원시값, string, number, boolean, symbol, null, undefined)를 observable state로 관리하지 못한다.
오로지 object, array, map만 observable state로 관리할 수 있다.
만약, 원시값을 observable state로 만들어주고 싶으면 원시값을 객체로 래핑하는 mobx의 함수를 사용해야 한다.(Mobx.box)

그래서, 아래와 같이 timerData라는 객체 자체를 전달해서 Timer컴포넌트 내부의 render function에서 timerData.secondsPassed와 같은 역참조가 일어나야 Mobx가 정상적으로 observable의 변화를 감지할 수 있다.

 

timerData.secondsPassed는 원래 0이라고 하는 을 가리키고 있었는데,

이게 1로 바뀌면 timerData.secondsPassed가 가리키는 메모리 주소가 변경된것이다.

 

Mobx가 감지하는게 바로 이런 화살표의 변화이다.

ReactDOM.render(<Timer timerData={timerData} />, document.body)

항상 observable state를 갖고 있는 객체를 전달하자.

reaction을 항상 dispose(메모리해제) 하는 습관을 들이자

autorun, observe, intercept와 같은 reaction들은 그들 내부에서 사용되고 있는 observable state들이 garbage collected되어야 본인도 garbage collected된다.

 

autorun과 같은 리액션들은 다음과 같이 dispose함수를 리턴한다.

this.handler = autorun(() => {
            doSomethingWith(this.price * this.amount)
        })

this.handler에는 dispose함수가 담겨있는데 이 함수를 실행시키면 autorun에 할당된 메모리가 해제된다.

const VAT = observable(1.2)

class OrderLine {
    @observable price = 10
    @observable amount = 1
    constructor() {
        // 아래 함수는 OrderLine클래스가 소멸되면 같이 가비지 콜렉팅 된다.
        // 내부에서 observable state만 사용하기 때문.
        this.handler = autorun(() => {
            doSomethingWith(this.price * this.amount)
        })
        
        // 하지만 아래 함수는 OrderLine클래스가 소멸되어도 메모리에 살아있다.
        // 외부에 있는 VAT를 참조하고 있기 때문이다.
        this.handler = autorun(() => {
            doSomethingWith(this.price * this.amount * VAT.get())
        })
		
        // 그래서 아래 dispose함수를 적절한 타이밍에 실행시켜서 메모리 해제 시켜주자.
        this.handler();
    }
}

 

댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함