MobX 코어 개념

yejineee·2021년 3월 1일
1

MobX

목록 보기
1/2

MobX 코어 개념

MobX에 대해 공부하며, 공식문서의 README와, The gist of MobX를 읽으며 내가 이해한대로 정리한 글이다.

🍎 MobX 흐름

MobX는 action으로 state가 바뀌었을 때, 단방향 으로 데이터가 흐르게 된다. 그 결과 영향을 받는 뷰들이 update된다.

Event가 발생하면 action으로 observable을 변경한다.

observable이 변경된 것은 그 값에 의존하고 있는 computed와 side effect에게 전달된다.

🍎 MobX에서의 핵심 3가지

1. State : MobX가 추적하게되는 데이터

  • state란?
    state는 application에서 가장 핵심이되는 데이터를 말하며, 주로 도메인과 관련한 state이다. 여행자보험 비교 사이트 플젝의 경우, State로는 보험상품내역 정도가 될 수 있겠다. 어떠한 데이터 구조여도 MobX에서는 상관없다. 중요한 것은 state가 계속 변하는 것을 MobX가 추적할 수 있도록 observable이라고 표기하는 것이다.
import { makeObservable, observable, action } from "mobx"

class Todo {
    id = Math.random()
    title = ""
    finished = false

    constructor(title) {
        makeObservable(this, {
            title: observable,
            finished: observable,
            toggle: action
        })
        this.title = title
    }

    toggle() {
        this.finished = !this.finished
    }
}

makeObservable안에서 하나씩 해당 state는 observable이라고 명시적으로 알려줄 수 있다. 더 간단한 방법은 makeAutoObservable을 사용하는 것이다.
observable state 만들기 - MobX

2. Actions : action으로 state를 변경

  • action이란?
    actionstate 를 변경하는 모든 코드를 말한다.

💡 observable을 변경하는 모든 코드에 action이라고 표기하는 것을 추천한다. 그렇게 해야 MobX가 자동으로 최적화를 위하여 transactions을 적용하기 때문이다.

-> 왜 action을 표기해야하는지에 대해 고민이었는데, 이 문장을 보고 그 의도를 알게되었다.

action을 사용하면 코드를 잘 구조화하는데 도움이 되며, 의도하지 않게 state를 변경하는것을 막아준다.

3. Derivations : 상태가 변경되었을 때 자동으로 호출

  • derivations란?
    state로 부터 자동으로 도출될 수 있는 모든 것은 derivation이라고 한다.
    그 예시로는 User Interface, observable을 사용하여 얻어낼 수 있는 데이터, 서버와 통신하는 것과 같은 백엔드 작업 등이 있다. derivation은 2가지로 구분할 수 있다.
    • Computed Value : 순수함수 를 통해 현재 observable state로 부터 도출될 수 있는 값
    • Reactions : state가 변경되었을 때, 자동으로 일어나게 되는 사이드 이펙트
  • derivation에서 알아야 할 것
    • 모든 derivation(computed, reaction)은 state가 변경되었을 때, 자동으로 그리고 atomic하게 업데이트된다. => update를 진행중인 값을 observe할 수는 없다.

    • 모든 derivation은 동기적으로 업데이트 되는 것이 디폴트다. 이는, action이 state를 변경한 직후에, computed 값을 사용하여도, 그 computed 값은 action이 변경한 observable로 재계산된 값이다.

      위의 글은 내가 해석하고 이해한대로 써보았는데, 틀린 얘기일 수 있으니 원문을 추가한다.

      All derivations are updated synchronously by default. This means that, for example, actions can safely inspect a computed value directly after altering the state

  • computed를 사용하여 값 만들기
    computed 값을 만드려면, (1) JS getter 함수인 get을 써야 하며, (2) makeObservable 안에서 computed라고 표기해주어야 한다. coumputed 값은 lazily update된다. 즉, 사용되지 않는 computed 값은 side effect(I/O)에 필요해질 때까지는, 업데이트 되지 않는다는 것이다. 만약, 뷰가 더이상 사용되지 않는다면, 자동으로 garbage collected될 것이다. computed는 자동으로 값을 update한 다음, 그 값을 캐싱하고 있다. computed는 순수해야 한다. 즉, computed에서 state를 변경시키면 안된다. 현재 state를 기반으로 값을 만들어낼 때 computed라고 표기해주는 것이 golden rule이라고 한다.
import { makeObservable, observable, computed } from "mobx"

class TodoList {
    todos = []
    get unfinishedTodoCount() {
        return this.todos.filter(todo => !todo.finished).length
    }
    constructor(todos) {
        makeObservable(this, {
            todos: observable,
            unfinishedTodoCount: computed
        })
        this.todos = todos
    }
}

위 예제에서 get이 있는 unfinishedTodoCount함수는 computed 값을 만들어낸다. 이 함수는 observable이 변경되면 자동으로 update가 된다. 여기서는 todos에 새로운 값이 들어온다던가, todos안에 있는 finished의 상태가 변경될 경우, 자동으로 새로운 값을 계산해낼 것이다.

  • reactions로 사이드 이펙트 만들기
    reaction 또한 observable이 변경되면 자동으로 실행된다. computed와의 차이점은, computed는 어떠한 결과를 반환한다면, reaction은 'side effect를 만들어내는 것만' 한다. 예를 들면, console.log를 찍기, 네트워크 요청, 등등...이 있을 것이다.

    명시적으로 호출되어야 하는 사이드이펙트와 같은 경우, 관련된 이벤트 핸들러에서 명시적으로 호출될 수 있다. 예를 들면, 폼을 제출했을 때 네트워크 요청을 해야하는 경우가 그렇다

  • Reactive한 리액트 컴포넌트
    observer로 리액트 컴포넌트를 감싸면, 컴포넌트를 reactive하게 만들 수 있다.
    observer를 통해 오늘 배운 점은 observer를 사용하면, 컴포넌트의 불필요한 리렌더링을 막을 수 있다는 점이다.

    observer converts React components into derivations of the data they render

    observer를 사용하면, 리액트 컴포넌트는 mobx가 렌더링해야하는 derivation으로 만들어버린다.

    MobX will simply make sure the components are always re-rendered whenever needed, and never more than that.

    MobX가 알아서, component가 사용하는 observable이 변하였을 때에만 컴포넌트를 리렌더링한다. 이는 해당 component의 부모가 리렌더링 되었을 때, 자식 component는 리렌더링하지 않아도 되는 경우에도 MobX가 알아서 리렌더링을 막아준다.

    따라서 observable을 사용하는 component를 observer로 감싸주어서 최적화를 시켜주어야 한다. 좀 더 최적화를 하자면, 최대한 component가 받는 observable은 해당 컴포넌트가 사용하는 props만 받아야 한다. 그렇지 않을 경우, 다른 Props가 변함으로써, 변하지 않은 props를 사용하는 컴포넌트도 같이 리렌더링되기 때문이다. 또한, dereference를 최대한 나중에 해야 한다.

  • 커스텀 reactions 만들기
    autorun이나 reaction, when 함수를 사용하여 reaction을 만들 수 있다.
    reaction이 그 안에서 주시하고 있는 observable이나 computed의 값이 변할 때, reaction인 함수가 자동으로 실행된다.

    아래 코드는 todos.unfinishedTodoCount 값이 바뀔 때마다 실행된다

// A function that automatically observes the state.
autorun(() => {
console.log("Tasks left: " + todos.unfinishedTodoCount)
})

이게 가능한 이유는 mobx의 특징 때문인데, 그 특징은 다음과 같다.

MobX가 추적하고 있는 함수가 실행되는 동안, 사용되는 observable property에 반응한다.

MobX reacts to any existing observable property that is read during the execution of a tracked function.

출처

0개의 댓글