MobX란?
애플리케이션 상태에서 파생되는 모든 것은 자동으로 되어야 합니다.
MobX는 functional reactive programming을 투명하게 적용함으로써 상태 관리를 쉽고 확장성 있게 만들어주는 검증된 라이브러리입니다.
즉, 상태 관리를 해주는 라이브러리다.
설치 명령어
Yarn: yarn add mobx
NPM: npm install --save mobx
사용해 보기
일단 나의 경우 위와 같이 flux를 사용해서 실시간으로 데이터를 받아오면 사건 목록에 리스트를 추가해줘야 한다.
그래서 사건들을 상태 관리를 하여 추가되는 즉시 렌더링을 해줄 것이다.
store 생성
가장 먼저 상태 관리를 해주기 위해 store를 만들 것이다.
참고로 파일 구조는 다음과 같다.
여기서 볼 것은 incidentStore.js와 stores.js다.
incidentStore는 위의 이미지의 카드 정보를 담아둘 것이고, stores.js는 모든 store를 관리해 줄 것이다.
incidentStore.js
import { makeAutoObservable } from "mobx";
class IncidentStore {
incidents = [];
constructor() {
makeAutoObservable(this);
}
get count() {
return this.incidents.length;
}
addIncident(incident) {
this.incidents.push(incident);
}
addIncidentToFront(incident) {
this.incidents.unshift(incident);
}
setIncidents(incidents) {
this.incidents = incidents;
}
}
export default IncidentStore;
이 Store에서는 insidents를 저장해 둘 것이고 count나 add, set은 말 그대로의 기능을 할 것이다.
여기서 중요한 부분이 생성자에서 makeAutoObservable을 해줘야 한다.
그래야지 상태가 변경되었을 때 observer 하고 있는 컴포넌트들을 리렌더링 해줄 것이다.
stores.js
import IncidentStore from "./incidentStore";
import CoordinateStore from "./coordinateStore";
import CenterStore from "./centerStore";
const stores = {
incidentStore: new IncidentStore(),
coordinateStore: new CoordinateStore(),
centerStore: new CenterStore(),
};
export default stores;
이 부분은 stores.js에서 모든 Store들을 등록해 두고 나중에 필요할 때 store를 한 js 파일에서 import 해서 사용해 주기 위해 만들었다.
Stores.js에서 꺼내서 사용
먼저 나의 경우 확장성을 생각해서 상위 컴포넌트에서 import 해주고 필요한 하위 컴포넌트에 store를 내려줄 생각이다.
MapRegister.js (상위 컴포넌트)
...
import stores from "./stores/stores";
import { observer } from "mobx-react";
const MapRegister = () => {
const { incidentStore, coordinateStore, centerStore } = stores;
...
return (
<div className="form-container">
...
<IncidentBoard stores={{ incidentStore, centerStore }} />
...
</div>
);
};
export default observer(MapRegister);
여기서 보면 stores를 import 한 후에
const { incidentStore, coordinateStore, centerStore } = stores;
필요한 store를 가져온다.
그리고 렌더링 쪽에서 자식 컴포넌트에 필요로 하는 store를 넘겨준다.
그리고 마지막에 observer를 해줘야 한다!
그래야 store 내부의 값이 변경되었을 때 리렌더링 해준다.
IncidentBoard.js (하위 컨트롤러)
...
import { observer } from "mobx-react";
const IncidentBoard = ({ stores }) => {
const { incidentStore, coordinateStore, centerStore } = stores;
...
useEffect(() => {
const eventSource = new EventSource(
`http://localhost:8080/api/flux/incident/subscribe`
);
eventSource.onmessage = (e) => {
const newIncident = JSON.parse(e.data);
incidentStore.addIncidentToFront(newIncident);
incidentAlert(newIncident);
};
return () => {
eventSource.close();
};
}, [incidentStore]);
...
return (
<div>
<div>
{incidentStore.incidents.length > 0 ? (
incidentStore.incidents.map((incident) => (
<div key={incident.id}>
<IncidentItem incident={incident} onCardClick={handleCardClick} />
</div>
))
) : (
<div style={{ textAlign: "center" }}>
<p>최근 3일간 등록된 사건이 없습니다.</p>
</div>
)}
</div>
...
</div>
</div>
);
};
export default observer(IncidentBoard);
({stores))를 통해 전달받은 stores를 받고 필요한 store를 꺼낸 후에
useEffect부터 서버로부터 FLUX로 데이터를 받았을 때 incidentStore.addIncidentToFront()를 통해 앞에 데이터를 추가하게 되면 맨 아래에 observer(incidentBoard)를 해주었기 때문에 FLUX 메시지가 들어오는 즉시 데이터가 추가될 것이다.
그리고 div에서 리렌더링 되는데 나의 경우 incidentStore.incidents를 하나씩 돌면서 item을 추가해 줬다.
그래서 중요한 것은
store, observer인 것 같다.
추가적으로 Swal(sweetalert2)을 사용하면서 메시지가 들어올 때 알림 처리를 해보자.
const incidentAlert = (incident) => {
Swal.fire({
title: "사건 발생",
text: incident.title,
icon: "warning",
confirmButtonText: "확인",
});
};
incidentBoard에서 보면 incidentAlert를 호출하는 부분이 있는데 Swal을 사용해서 alert 해주는 것이다.
띄워보면 아래와 같이 모달창 알림을 띄운다.
상태관리 정말 편한 것 같다.