간단한 전역 상태 관리 훅 만들어보기.
참고 문서
배경 지식
- Local state management
- Global state management
해당 예제는 count의 증가를 화면에 반영하지 않는다.
import React from 'react';
// 전역 변수
let count = 0;
function Counter(props) {
let incrementCount = e => {
++count;
console.log(count);
};
return (
<div>
Count: {count}
<br />
<button onClick={incrementCount}>Click</button>
</div>
);
}
ReactDOM.render(<Counter />, document.querySelector('#root'));
가장 간단한 전역 변수 사용 방법
import React from 'react';
// use global variable to store global state
let count = 0;
function Counter(props) {
const [, setState] = useState();
let incrementCount = e => {
++count;
console.log(count);
// 해당 함수를 호출하기만 하면 강제로 리렌더링을 유발한다.
setState({});
};
return (
<div>
Count: {count}
<br />
<button onClick={incrementCount}>Click</button>
</div>
);
}
ReactDOM.render(<Counter />, document.querySelector('#root'));
문제 : 만약 이렇게 된다면?
import React from 'react';
// 전역 변수
let count = 0;
function Counter1(props) {
const [, setState] = useState();
let incrementCount = e => {
++count;
setState({});
};
return (
<div>
Count: {count}
<br />
<button onClick={incrementCount}>Click</button>
</div>
);
}
function Counter2(props) {
const [, setState] = useState();
let incrementCount = e => {
++count;
setState({});
};
return (
<div>
Count: {count}
<br />
<button onClick={incrementCount}>Click</button>
</div>
);
}
function Counters(props) {
return (
<>
<Counter1 />
<Counter2 />
</>
);
}
ReactDOM.render(<Counters />, document.querySelector('#root'));
해결책 : 전역 변수가 모든 리렌더링 대상 컴포넌트에게 알리면 된다.
- 이것이
옵저버
패턴이다.
- subscriber가 subject다.

// 해당 함수는 객체 생성자 함수임
function GlobalState(initialValue) {
this.value = initialValue; // 전역 변수.
this.subscribers = []; // 구독자 리스트 : 리렌더링할 컴포넌트들.
this.getValue = function () {
// 전역 변수 값을 얻는 메소드
return this.value;
};
this.setValue = function (newState) {
// 전역 상태를 업데이트하는 메소드
if (this.getValue() === newState) {
// No new update
return;
}
this.value = newState; // 전역 상태 업데이트
this.subscribers.forEach(subscriber => {
// 구독자들에게 변화를 알림.
subscriber(this.value);
});
};
this.subscribe = function (itemToSubscribe) {
if (this.subscribers.indexOf(itemToSubscribe) > -1) {
// 이미 구독중인 컴포넌트면 아무 일도 하지 않음
return;
}
// 해당 컴포넌트를 구독함.
this.subscribers.push(itemToSubscribe);
};
this.unsubscribe = function (itemToUnsubscribe) {
// This is a function for unsubscribing from a global state
this.subscribers = this.subscribers.filter(
subscriber => subscriber !== itemToUnsubscribe,
);
};
}
정리
- subscribe, unsubscribe로 pub/sub 메커니즘 활용
- setValue로 전역변수 변경. 전체 업데이트
- getValue로 전역변수 얻어옴.
- subscriber인 컴포넌트는 setValue 이벤트를 발행함. 그러면 observer GlobalState가 rerender 이벤트를 퍼블리시함.
import { useState, useEffect } from 'react';
function useGlobalState(globalState) {
const [, setState] = useState();
const state = globalState.getValue();
function reRender(newState) {
// 리렌더링을 강제로 유발함
setState({});
}
useEffect(() => {
// 변화 시 리렌더를 강제 유발하기 위함.
globalState.subscribe(reRender);
return () => {
// 언마운트 시 구독 해제
globalState.unsubscribe(reRender);
};
});
function setState(newState) {
// 전역 상태를 업데이트하면 퍼블리셔인 GlobalState가 업데이트를 publish함.
globalState.setValue(newState);
}
return [State, setState];
}
Observer Vs PubSub
- 추가로 해볼만한 것들
- globalState를 클로저로 집어넣기.
- reRender부분에 로직을 넣어 redux connect 구현.
-...