만족

[React] MobX 실제로 사용해보기 본문

[React] MobX 실제로 사용해보기

FrontEnd/React Satisfaction 2020. 10. 10. 03:38

satisfactoryplace.tistory.com/154?category=829221

 

 

[React] MobX 개요

MobX란? 상태 관리 라이브러리다. 비슷한 종류의 대표적인 상태 관리 라이브러리로 redux가 있다. 개인적으로 redux의 지독한(...) 보일러플레이트에 깊은 빡침을 느껴 mobx를 사용해보려고 한다. 설명

satisfactoryplace.tistory.com

위 포스트에서 이어지는 내용이다.

 

이번엔 MobX로 실제로 어떤 것을 해볼 수 있는지를 살펴보자.

 

우선 observerable state의 위치에 따른(내부/외부) state사용법에 대해 알아볼 것이다.

 

class Timer {
    secondsPassed = 0

    constructor() {
        makeAutoObservable(this)
    }

    increaseTimer() {
        this.secondsPassed += 1
    }
}

const myTimer = new Timer()

위의 Observable한 state인 myTimer를 가지고 설명할 것이다.

 

외부 상태를 사용하는 컴포넌트

props를 사용하는 경우

const TimerView = observer(({ timer }) => <span>Seconds passed: {timer.secondsPassed}</span>)

// Pass myTimer as a prop.
ReactDOM.render(<TimerView timer={myTimer} />, document.body)

myTimer를 props로 전달하고, 이를 받는 컴포넌트는 observer()를 이용해 컴포넌트를 래핑되어 myTimer의 상태 변화에 따라 사이드이펙트가 발생한다.

 

전역변수를 사용하는 경우

// No props, `myTimer` is directly consumed from the closure.
const TimerView = observer(() => <span>Seconds passed: {myTimer.secondsPassed}</span>)

ReactDOM.render(<TimerView />, document.body)

observer로 래핑된 컴포넌트 내에서 myTimer의 상태 변화에 따라 사이드이펙트가 발생한다.


React Context를 사용하는 경우

const TimerView = observer(() => {
    // Grab the timer from the context.
    const timer = useContext(TimerContext) // See the Timer definition above.
    return (
        <span>Seconds passed: {timer.secondsPassed}</span>
    )
})

ReactDOM.render(
    <TimerContext.Provider value={new Timer()}
        <TimerView />
    </TimerContext.Provider>,
    document.body
)

컨텍스트에 상태값을 넣고 Provider 하위의 observer로 래핑된 컴포넌트에서 useContext를 이용해 

observable state를 가져와 해당 상태 변화에 따라 사이드이펙트가 발생한다.

 

내부 상태를 사용하는 컴포넌트

이 항목은 따로 설명하지 않을 것이다.

 

필요한 경우 아래 링크를 참조할 것.

mobx.js.org/react-integration.html

 

React integration · MobX

 

 

mobx.js.org

 

예시- 로그인 상태 관리

간단한 예제를 위해 id/pw를 직접 저장했으나, 실제로는 그렇게 해서는 안되고

token을 저장하거나 하는 방식으로 인증을 처리한다는 점을 알아두길 바란다.

 

auth.js (observable)

class Auth{
  constructor(){
    makeAutoObservable();
  }
  
  id= '';
  pw= '';

  login(){
    id= 'testId';
    pw= 'myPassword';
  }
  
  logout(){
    id='';
    pw='';
  }
}

const auth= new Auth();
export default auth;

Auth.js (observer component)

const Auth= ()=>{
	return <div>
    	아이디: {auth.id}
        <br/>
        비밀번호: {auth.pw}
        <br/><br/>
        <span onClick={e=> auth.login()}>로그인</span>
        <span onClick={e=> auth.logout()}></span>
    </div>
};

export default observer(Auth);

 

이렇게 하면 auth.js에서 export한 auth state를 observer로 감싸진 모든 컴포넌트 내에서 전역적으로 쓸 수 있게 된다.

 

가령 AuthState.js (observer component) 파일이 다음과 같이 생겼다고 하면,

const AuthState= ()=>{
	return <p>
    	{auth.id && auth.pw? '로그인 되었습니다': '로그인되지 않았습니다'}
    </p>
};

export default observer(AuthState);

위와 같이 사용할 수도 있다.

 

주의: props로 전달할 때

만약 Auth.js와 AuthState.js가 다음과 같이 생겼다고 해보자.

const Auth= ()=>{
	return <div>
    	<AuthState auth={auth}/>
        <br/><br/>
        <span onClick={e=> auth.login()}>로그인</span>
        <span onClick={e=> auth.logout()}></span>
    </div>
};

export default observer(Auth);
const AuthState= ({auth})=>{
	return <p>
    	{auth.id && auth.pw? '로그인 되었습니다': '로그인되지 않았습니다'}
    </p>
};

//더 이상 observer()로 감싸지 않는다
export default AuthState;

 

AuthState는 login()/logout()에 따라 정상적으로 작동하지 않을 수 있다.

 

왜냐하면 auth는 observable state임에도 AuthState는 observer()로 래핑되지 않았기 때문이다.

 

이에 대한 해법은 다음과 같다.

 

const Auth= ()=>{
	return <div>
    	<AuthState auth={{id: auth.id, pw: auth.pw}}/>
        <br/><br/>
        <span onClick={e=> auth.login()}>로그인</span>
        <span onClick={e=> auth.logout()}></span>
    </div>
};

export default observer(Auth);

AuthState에 props를 전달할 때 auth를 뜯어서 새로운 object를 만들어서 전달하는 방법이 있고

 

const Auth= ()=>{
	return <div>
    	<AuthState auth={toJS(auth)}/>
        <br/><br/>
        <span onClick={e=> auth.login()}>로그인</span>
        <span onClick={e=> auth.logout()}></span>
    </div>
};

export default observer(Auth);

toJS를 이용해 객체를 역직렬화해 전달하는 방법도 있다.

 

주의: 콜백 형태로 렌더할 때

Renderer.js

const Renderer= ({onRender})=>{
	return onRender();
};

export default Renderer;

Auth.js

const Auth= ()=>{
	return <div>
    	<Renderer onRender={()=> <p>아이디: {auth.id}</p>}/>
        <Renderer onRender={()=> <p>비밀번호: {auth.pw}</p>}/>
        <br/><br/>
        <span onClick={e=> auth.login()}>로그인</span>
        <span onClick={e=> auth.logout()}></span>
    </div>
};

export default observer(Auth);

 

다음과 같은 컴포넌트가 있다고 해 보자.

 

이 경우에도 Renderer와 렌더링 될 컴포넌트가 observer가 아니기 때문에 정상적으로 작동하지 않는다.

 

이럴 때는 <Observer></Observer>로 감싼 뒤, 렌더링할 내용을 다시 한번 콜백으로 만든다.

 

const Auth= ()=>{
	return <div>
    	<Renderer onRender={()=> <Observer>{()=><p>아이디: {auth.id}</p>}</Observer>}/>
        <Renderer onRender={()=> <Observer>{()=><p>비밀번호: {auth.pw}</p>}</Observer>}/>
        <br/><br/>
        <span onClick={e=> auth.login()}>로그인</span>
        <span onClick={e=> auth.logout()}></span>
    </div>
};

export default observer(Auth);

이제 정상작동한다.

 

그 외

http.js

export const getUserInfo= ()=>{
	const token= auth.token;
    return axios.get('https://test.com', {headers: {Authorization: 'Bearer '+ token}});
};

 

위와 같이 http.js파일에서 요구하는 인증 헤더값을 함수 파라미터가 아닌

global observerable state로부터 불러와서 사용할 수도 있다.

 

여러모로 편리한 점이 많으니 한번쯤 사용해보는것을 추천한다.

 

 

'FrontEnd > React' 카테고리의 다른 글

[React] Hook과 함께 HOC 사용해보기  (1) 2020.11.04
[React] 404 페이지 만들기  (0) 2020.10.20
[React] MobX 개요  (0) 2020.10.04
[React] 애드센스 합격 과정  (22) 2020.09.14
[React] 페이지별 메타 태그 추가  (2) 2020.09.14


Comments