MobX 정리
MobX
https://ko.mobx.js.org/README.html
MobX 는 action 이 state 를 변경하는 단방향 데이터 흐름을 사용하며, 영향을 받는 모든 View 를 업데이트한다.
MobX Core
- Observable : state 의 변화를 감시하여, state 를 저장 및 추적한다.
- Action : state 를 수정하는 메서드
- Computed : state 의 변화로 인해 계산된 값. 일종의 캐싱으로 생각하면 된다. 내부에서 사용하는 state 가 변경되었을때만 새로 계산해서 계산값을 저장해놓고 사용한다.
- Reaction : observable state 를 변경하면 그에 따른 파생값(computed)이 계산된다. 파생값을 사용하는 컴포넌트를 변경하는 Reaction 작업이 일어난다.
- 상태(state)
- 앱을 구동하는 데이터
- 변경하려는 속성을 추적하기 위해 observable 로 표시되어야 함
- mobx 라이브러리의 useObserver 함수는 리액트의 useState 를 사용한다. forceUpdate 를 통해, 컴포넌트의 첫 마운트 시, observable 변경 발생 시 이 함수를 실행하여 리렌더링을 수행한다.
- 동작(action)
- 사용자 이벤트, BE 데이터 푸시 등과 같이 state 를 변경하는 로직
- observable 을 변경하는 코드는 action 으로 표시되어야 한다.
- 파생(derivation)
- state 에서 유도된 ‘객체’
- 반응 함수를 의미한다.
- state 에서 유도된 ‘객체’
React Observer HOC
- observer 함수를 이용하여 컴포넌트를 감싸서 반응형으로 만들 수 있음
- HOC 란 리액트 컴포넌트를 인자로 받아서 새로운 리액트 컴포넌트를 반환함
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import * as React from "react"
import { render } from "react-dom"
import { observer } from "mobx-react-lite"
const TodoListView = observer(({ todoList }) => (
<div>
<ul>
{todoList.todos.map(todo => (
<TodoView todo={todo} key={todo.id} />
))}
</ul>
Tasks left: {todoList.unfinishedTodoCount}
</div>
))
const TodoView = observer(({ todo }) => (
<li>
<input type="checkbox" checked={todo.finished} onClick={() => todo.toggle()} />
{todo.title}
</li>
))
const store = new TodoList([new Todo("Get Coffee"), new Todo("Write simpler code")])
render(<TodoListView todoList={store} />, document.getElementById("root"))
observer 를 이용해서 derivation 을 적용한 예시 코드.
TodoListView 는 todoList 의 상태를 관찰하고, TodoView 는 todo 의 상태를 관찰한다. 이렇게 observable 한 state 는 Derivation 인 것이다. todoList.unfinishedTodoCount 는 todoList 의 상태를 기반으로 계산됐고, computed 로 취급된다.
클래식 MVC 패턴을 적용해보자
스토어
- 컴포넌트의 로직과 state 를 독립적으로 테스트할 수 있는 단위로 만드는 것
- 도메인 state 저장소와 ui state 저장소가 있어야 한다
- 도메인 스토어 : 애플리케이션의 모든 데이터가 저장되는 곳
- 도메인 객체 : 도메인 객체는 자체 클래스를 사용하여 표현해야 한다.
도메인 스토어
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import { makeAutoObservable, autorun, runInAction } from "mobx"
import uuid from "node-uuid"
export interface Todo {
id : number;
content : string;
checked : boolean;
}
export class TodoStore {
authorStore // 작성자 확인 스토어
transportLayer // 서버 요청 레이어
todos = Todo[]
isLoading = true
constructor(transportLayer, authorStore) {
makeAutoObservable(this) // 객체를 observable 로 만든다.
this.authorStore = authorStore
this.transportLayer = transportLayer
this.transportLayer.onReceiveTodoUpdate(updatedTodo =>
this.updateTodoFromServer(updatedTodo)
)
this.loadTodos()
}
// 서버에서 모든 todo를 가져옵니다.
loadTodos() {
this.isLoading = true
this.transportLayer.fetchTodos().then(fetchedTodos => {
runInAction(() => {
fetchedTodos.forEach(json => this.updateTodoFromServer(json))
this.isLoading = false
})
})
}
// 서버의 정보로 Todo를 업데이트합니다. Todo가 한 번만 존재함을 보장합니다.
// 새로운 Todo를 생성하거나 기존 Todo를 업데이트하거나
// 서버에서 삭제된 Todo를 제거할 수 있습니다.
updateTodoFromServer(json) {
let todo = this.todos.find(todo => todo.id === json.id)
if (!todo) {
todo = new Todo(this, json.id)
this.todos.push(todo)
}
if (json.isDeleted) {
this.removeTodo(todo)
} else {
todo.updateFromJson(json)
}
}
// 클라이언트와 서버에 새로운 Todo를 생성합니다.
createTodo() {
const todo = new Todo(this)
this.todos.push(todo) // 상태를 조작할 때 객체 불변성을 지킬 필요 없다. mobx 에서 상태의 변화를 자동으로 감지한다.
return todo
}
// Todo가 어떻게든 삭제되었을 때 클라이언트 메모리에서 삭제합니다.
removeTodo(todo) {
this.todos.splice(this.todos.indexOf(todo), 1)
todo.dispose()
}
}
// 객체를 생성하고, 생성된 객체를 export 한다
export const todoStore = new TodoStore();
컴포넌트
1
2
3
4
5
6
7
import { observer } from 'mobx-react-lite';
import { todoStore } from './CreditManageStore';
export const TodoListPage = observer(() => {
const { todos, loadTodos, createTodo, removeTodo } = todoStore;
...
})
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.