< React(리액트) : 상태관리 라이브러리 MobX 개념 >


* 개념 학습 기본 code

 ; action은 observable 요소 값을 변경시킴.

 ; transaction의 parameter로 action 함수 여러개로 구성된 함수를 전달하면, 전달된 action들의 집합 전체가 반영된 후, MobX의 주시 함수들이 실행됨.

 ; action이 observable 요소 값을 변경하면, computed는 observe를 사용해 요소 값의 변화를 관찰하고, computed 결과 값이 계산되어 cache에 저장됨.

 ; action이 observable 요소 값을 변경하면, 요소 값을 주시하도록 지정된 reaction이 발동되어 reaction의 parameter로 지정된 함수가 실행됨.

 ; autorun은 요소값을 observe로 주시하지 않아도 autorun 내부에 지정된 함수는 자동으로 요소값을 주시하다가 변경이 발생하면 지정된 함수가 실행됨.

 ; @를 사용하여 변수, 함수 등을 decorate 시켜주면(@observable, @computed, @action 등), observable, computed, action 속성을 간단히 적용시킬 수 있음.


import { observable, reaction, computed, autorun } from 'mobx';

// observable() 함수의 parameter 로 주시당할 생성 객체를 전달
// 생성된 주시당할 객체는 const 로 할당
const calculator = observable({
a: 0,
b: 0
})

// reaction() 함수로 특정 값을 주시값 변경시 특정 함수 실행
// reaction() 함수의 첫번째 parameter는 주시 값 지정 함수, 두번째 parameter는 실행함수를 전달
reaction(
() => calculator.a,
(value, reaction) => {
console.log(`a 값이 ${value} 로 바뀌었네요!`);
}
);

reaction(
() => calculator.b,
value => {
console.log(`b 값이 ${value} 로 바뀌었네요!`);
}
);

// computed() 함수로 특정값(sum) 캐싱해서 const 로 할당
// computed() 함수의 parameter는 연산 결과를 return 하는 함수
// observe() 함수를 이용해서 computed 결과값이 연산 요소값을 주시하도록 지정
const sum = computed(
() => {
console.log(`computed 연산이 실행됩니다. a: ${calculator.a}, b: ${calculator.b}`);
return calculator.a + calculator.b;
}
);

sum.observe(
() => calculator.a
);
sum.observe(
() => calculator.b
);

// **** autorun 은 함수 내에서 조회하는 값을 자동으로 주시함.
// computed로 만들어진 sum의 내부 요소는 자동 주시함.
autorun(() => console.log(`오토런 : a 값이 ${calculator.a} 로 바뀌었네요!`));
autorun(() => console.log(`오토런 : b 값이 ${calculator.b} 로 바뀌었네요!`));
autorun(() => console.log(`sum.get 값 : ${sum.get()}`));

// 값이 최초로 변경되는 순간 값이 변경되기 전 기준으로 computed 함수가 최초 실행된 후
// 변경된 값 기준으로 다시 실행됨.
calculator.a = 10;
calculator.b = 20
// **** 여러번 조회해도 computed 안의 함수를 다시 호출하지 않지만..
console.log(sum.value);
console.log(sum.value);
// 내부의 값이 바뀌면 다시 호출 함
calculator.a = 20;
console.log(sum.value);



* 실행 결과

computed 연산이 실행됩니다. a: 0, b: 0
오토런 : a 값이 0 로 바뀌었네요!
오토런 : b 값이 0 로 바뀌었네요!
sum.get 값 : 0
a 값이 10 로 바뀌었네요!
computed 연산이 실행됩니다. a: 10, b: 0
sum.get 값 : 10
오토런 : a 값이 10 로 바뀌었네요!
b 값이 20 로 바뀌었네요!
computed 연산이 실행됩니다. a: 10, b: 20
sum.get 값 : 30
오토런 : b 값이 20 로 바뀌었네요!
a 값이 20 로 바뀌었네요!
computed 연산이 실행됩니다. a: 20, b: 20
sum.get 값 : 40
오토런 : a 값이 20 로 바뀌었네요!



* mobx 를 활용한 counter 기본 예제 code


 -- 앱 생성 및 관련 라이브러리 설치 --


$ npm create-react-app mobx_std

$ cd mobx_std

$ yarn add mobx mobx-react

$ yarn start



-- @(decorator) 를 사용하려면 babel 설정을 커스터마이징해야 함. --


프로젝트 eject 를 해야 babel 설정을 변경할 수 있음.


1. 프로젝트 eject


$ yarn eject

$ yarn # 가끔 일부 모듈이 설치되지 않는 증상, 이걸 한번 더 실행해서 확실하게 설치

$ yarn add babel-preset-mobx


2. babel 설정 커스터마이징


package.json - babel 부분 수정


  "babel": {

    "presets": [

      "react-app",

      "mobx"

    ]

  },


3. 서버 재실행

yarn start


//index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));

serviceWorker.unregister();


//App.js

import React, { Component } from 'react';
import Counter from './Counter';

class App extends Component {
render() {
return (
<div>
<Counter />
</div>
);
}
}

export default App;


//Counter.js

import React, {Component} from 'react'
import {observable, action} from 'mobx'
import {observer} from 'mobx-react'

@observer
class Counter extends Component {

@observable
number = 0;

@action
increase = () => {this.number++;}

@action
decrease = () => {this.number--;}

render(){
return(
<div>
<h1>{this.number}</h1>
<button onClick={this.increase}>더하기 1</button>
<button onClick={this.decrease}>빼기 1</button>
</div>
);
}

}

export default Counter;



* mobx 를 활용한 counter 기본 예제 code에 스토어 적용


// src/stores/CounterStore.js
// 스토어 생성하기

import { observable, action } from 'mobx';

export default class CounterStore {
@observable number = 0;

@action increase = () => {
this.number++;
}

@action decrease = () => {
this.number--;
}
}


// src.index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'mobx-react';
// MobX 에서 사용하는 Provider
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import CounterStore from './stores/counterStore';
// 스토어 불러옵니다.


const counterStore = new CounterStore();
// 스토어 인스턴스를 만들고

ReactDOM.render(
<Provider CounterStore={counterStore}>
{/* Provider 에 위에서 만든 스토어 instance를 props 로 넣어줍니다.
이렇게 App component로 전달된 props는 App의 자식 component가
@inject('CounterStore') 로 가지고 와서 this.props.CounterStore로
사용하거나,@inject(stores => ({
number:.... 형태로 스토어의 구성 요소별로 필요한 요소만 가지고와서
this.props.number 형태로 개별적으로 사용할 수 있습니다.
전자는 Counter.js, 후자는 Counter2.js 참고 */}
<App />
</Provider>,
document.getElementById('root')
);

serviceWorker.unregister();


// src/App.js
/* App component는 index.js 에서 Provider Wrapper 를 통해 props로 전달 받은
스토어를 가지고 있으며, App 의 자식 component 들은 inject decorator를 사용하여
전달받은 스토어를 자신의 component에 주입하여 사용할 수 있다.*/

import React, { Component } from 'react';
import Counter from './Counter';
import Counter2 from './Counter2';

class App extends Component {
render() {
return (
<div>
<Counter />
<Counter2 />
</div>
);
}
}

export default App;


// src/Counter.js
// 스토어 전체를 inject 해서 사용하는 방법

import React, { Component } from 'react';
import { observer, inject } from 'mobx-react';

@inject('CounterStore')
@observer
class Counter extends Component {
render() {
const { CounterStore } = this.props;
return (
<div>
<h1>{CounterStore.number}</h1>
<button onClick={CounterStore.increase}>+1</button>
<button onClick={CounterStore.decrease}>-1</button>
</div>
);
}
}

export default Counter;


// src/Counter2.js
// 스토어 중 필요한 개별요소별로 inject 해서 사용하는 방법

import React, { Component } from 'react';
import { observer, inject } from 'mobx-react';

// **** 함수형태로 파라미터를 전달해주면 필요한 특정 값만 받아올 수 있음.
@inject(stores => ({
number: stores.CounterStore.number,
increase: stores.CounterStore.increase,
decrease: stores.CounterStore.decrease,
}))
@observer
class Counter2 extends Component {
render() {
const { number, increase, decrease } = this.props;
return (
<div>
<h1 style={{color: "red", backgroundColor: "green"}}>{number}</h1>
<button onClick={increase}>+1</button>
<button onClick={decrease}>-1</button>
</div>
);
}
}

export default Counter2;






+ Recent posts