티스토리 뷰

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

저번 글에 이은 mobX 3탄입니다.  저번엔 MobX 의 API에 대한 이야기는 제쳐두고, 갑자기 React MobX의 설계론에 대한 이야기를 해버렸습니다.  이번 편에는 개인적으로 MobX의 조금 알기 힘들고 조심해야겠다고 느낀 기본적인 부분에 대해서 적어보겠습니다.

【DevTools】

이미 MobX의 코드를 만들어본 분중에, DevTools를 도입하지 않은 분이 있다면 꼭 도입해봐주십시오.  이 페이지에 <DevTools / >를 마운트해서 도입하는 방법이 소개되어있습니다 .ChromeExtension를 도입해두면, 마운트없이 같은 기능을 이용가능하게 됩니다.  본편의 내용을 이해하는데 도움이 될것으로 보입니다.

observable형에 대해서

observable값에는, JS프리모티브, 참조, 플레인오브젝트, 클래스 인스턴스, 배열, 맵 등이 있습니다. @observable은、항상 최적의 observable형을 작성하려고 합니다. 그렇기 때문에 의도하지 않더라도 @observable값으로 만들어버릴 위험이 있습니다. 

observable형은, 계층을 어디까지 거슬러 올라갈 수 있는 값인지, modifiers에서 지정하는 것이 가능합니다.  modifier에는 「ref」「shallow」「deep」가 있으며,  아무것도 지정하지 않으면, 항상.deep로 설정됩니다. 

store.js
class Store {
  @observable.ref      collectionRef = []
  @observable.shallow  collectionShallow = []
  @observable/*.deep*/ collectionDeep = []
}

.ref는 가장 얕은 추적을 합니다.  아래 적은 것과 같은 동작입니다.  Object의 참조가 변하는 것을 트리거로 observer에 전달합니다. 

store.js

class Store { @observable.ref collection = [] @action editCollection = () => { // 참조가 변경되었기 때문에 전달한다. (새로운 배열을 참조) this.collection = [{ name: 'myName' }] // 참조가 그대로 이기때문에 전달하지 않는다. this.collection[0] = { name: 'myName' } } }

.shallow는 1계단까지 추적, 변경이 전달됩니다.  참조의 변경에 대해서도 전달됩니다.

store.js

class Store { @observable.shallow user = { name: 'myName', friends: [{ name: 'firendName' }] } @action editUser = () => { //1계단째 이기때문에 전달한다. this.user.name = 'newName' // 2계단 이상 차이 나기때문에 전달하지 않는다. this.user.friends[0].name = 'newFriendName' // 참조가 변경되었기 때문에 전달한다. this.user = { name: 'myName', friends: [{ name: 'newFriendName' }]} } }

.deep는 계층은 단계에 관계없이 추적하여, 변경이 전달가능합니다.  기본 동작이 이것으로 되어있습니다.   조심해야 하는 것이 샘플 코드의 아래 부분, 새로운 Object를 참조하는 부분입니다. 

store.js
class Store {
  @observable user = {
    name: 'myName',
    friends: [{ name: 'firendName' }]
  }
  @action editUser = () => {
    // 전달한다 (1번만)
    this.user.friends[0].name = 'newFriendName'
    // 전달한다. (3번이나)
    this.user = { name: 'myName', friends: [{ name: 'newFriendName' }]}
  }
}

deep에서 추적하기때문에 아래의 세가지가 observable값으로 판단되어
observer에 3번이나 전달이 보내지게 됩니다.  이것은this.user를 참조하고 있는 React컴포넌트가, 3번 re-rendering 되는 것을 의미합니다. 

  • this.user.name
  • this.user.friends
  • this.user.friends[0]

이점을 제대로 파악해두지 않으면,  눈치 채지 못한 사이에 퍼포먼스가 떨어지게 됩니다.  API로부터 거대한 json 배열을 취득하여 @observable = [] 등을 대입하게 되면, 엄청나게 느려집니다.  이런 경우에는  @observable.ref = [] 와 같이、요건에 응하는 modifier를 부여합시다.

또한 위에 적은것은, 전달의 여부에 대한 이야기로, 내용은 변하고 있습니다.  별도 컴포넌트의 변화에 의해 re-redering되는 때는 변화하기때문에, 그 동작은 조금 헷갈릴지도 모르겠습니다. 

action의 존재 의미

제대로 쓰고 싶은 사람이라면 생각해두면 좋을 것 같습니다.

우선  useStrict(true) 를 선언해 봅시다. 

main.js
import { useStrict } from 'mobx'
useStrict(true)

@action 에 표현되어 있지 않은 함수로부터 observable값을 변경하려고 하면,  혼납니다. useStrict(true) 선언도 @action 표현의 사용도, optional 이지만,  로그 추적이 가능해 지기때문에, 개인적으로는 action도 strict모드를 사용하는 것을 선호합니다.  

Error: [mobx] Invariant failed: Since strict-mode is enabled, changing observed observable values outside actions is not allowed. Please wrap the code in an `action` if this change is intended. 
  • action 을 부여하는 것으로 state를 변경하는 함수라는 것을 명시적으로 선언한다.
  • observable값을 변경하거나 콜백을 동반하는 함수는 action사용을 추천합니다.  
  • DevTools에서 로그 출력이 됩니다. 
  • strict모드를 유효화한 경우, action을 경유하지 않는 observable값의 변경에 대해 경고를 볼수 있다. 
  • 콜백과 같은 경우에, observable값을 변경하려고 하는 경우, runInAction 함수로 래핑하지 않으면 경고를 받게 됩니다. 
  • runInAction 함수는 제1 매개변수에 devTools의 로그가 설정됩니다. 

참고로, @action표현도 아래와 같이 로그용의 문자열을 지정하는 것이 가능합니다. 

역) 値を1追加 : 값을 1 추가 

someStore.js
@action('値を1追加') increment = () => this.count++

image

공지사항
최근에 올라온 글
최근에 달린 댓글
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
글 보관함