<A>
<B>
<C>
프롭 드릴링의 문제
- 값의 출처 파악 어려움 , 컴포넌트 중첩이 깊숙히 될수록
- B는 A와 C 사이에 위치했다는 이유만으로 고유의 역할과 무관하게 메시지를 전달해야함
이벤트 에미터 패턴을 통해 해결 할 수 있다.
두 객체 간의 메시지를 비교적 자유롭게 주고 받을 수 있는 통로 역할이다.
const createEventEmitter = (value) => {
let handlers = [];
const on = (handler) => handlers.push(handler);
const off = (handler) => {
handlers = handlers.filter((h) => h !== handler);
};
const get = () => value;
const set = (newValue) => {
value = newValue;
handlers.forEach((handler) => handler(value));
};
return {
on,
off,
get,
set,
};
};
export default createEventEmitter;
const eventEmitter = createEventEmitter(0);
const logger = (value) => console.log("logger", value);
eventEmitter.on(logger);
console.log(eventEmitter.get());
eventEmitter.set(1);
eventEmitter.set(2);
eventEmitter.off(logger);
eventEmitter.set(3);
console.log(eventEmitter.get());
eventemitter는 closure를 사용한것이다. createEventEmitter를 생성할 때 0을 value로 주면 return 값으로 주는 on,off, get,set 함수들에는 value==0 인 상태의 lexical scope이 저장이 된다.
모든 함수는 함수가 선언이 될때의 lexical scope를 기억한다.
get도 마찬가지로 내부함수로 선언되는 순간에 Environment에 현재 lexical scope인 value == 0이 저장이 된다.
set도 마찬가지이다. 그래서 예시 코드를 실행 시키면 의도한대로 초기값이 0인 상태로 잘 동작을 하게 된다.
컨택스트
const MyReact = (function () {
function createContext(initialValue) {
const emitter = createEventEmitter(initialValue);
class Provider extends React.Component {
componentDidMount() {
emitter.set(this.props.value);
}
componentDidUpdate() {
emitter.set(this.props.value);
}
render() {
return <>{this.props.children}</>;
}
}
class Consumer extends React.Component {
constructor(props) {
super(props);
this.state = {
value: emitter.get(),
};
this.setValue = this.setValue.bind(this);
}
setValue(nextValue) {
this.setState({ value: nextValue });
}
componentDidMount() {
emitter.on(this.setValue);
}
componentWillUnmount() {
emitter.off(this.setValue);
}
render() {
return <>{this.props.children(this.state.value)}</>;
}
}
return {
Provider,
Consumer,
};
}
return {
createContext,
};
})();
export default MyReact;
Consumer에서는 componetdidMount에서 setState를 emitter 에 등록을 하고
Provider 에서는 componentdidMount에서는 context를 emitter에 set을 호출하면서 Consumer가 등록한 handler
호출해준다.
내부 component부터 compoentDidMount가 호출이 된다.
const countContext = MyReact.createContext({
count: 0,
setCount: () => {},
});
class CountProvider extends React.Component {
constructor(props) {
console.log("CountProvider construtor");
super(props);
this.state = {
count: 0,
};
}
render() {
const value = {
count: this.state.count,
setCount: (nextValue) => this.setState({ count: nextValue }),
};
return (
<countContext.Provider value={value}>
{this.props.children}
</countContext.Provider>
);
}
}
const Count = () => {
return (
<countContext.Consumer>
{(value) => {
console.log("CountComponent", value);
return <div>{value.count}</div>;
}}
</countContext.Consumer>
);
};
const PlusButton = () => {
return (
<countContext.Consumer>
{(value) => {
console.log("PlustButtonComponent", value);
return (
<button onClick={() => value.setCount(value.count + 1)}>
+ 카운트 올리기
</button>
);
}}
</countContext.Consumer>
);
};
export default () => (
<CountProvider>
<Count />
<PlusButton />
</CountProvider>
);
호출 순서는 위와 같다. Provider 생성자 -> Consumer 생성자
-> Consumer ComponentDidMount(emitter#on 에서 handler등록 ,Producer에서 제공해주는 value를 받기 위함이다. 초기에는 createContext에서 제공해준 value값을 받아오기 때문이다.)
-> Producer ComponentDidMount -> Producer의 componentDidMount에서 emitter#set 을 통해서 setValue가 호출이 된다.
이러면 Producer가 render하기 전에 생성한 value (count, setCount) 들이 Consumer에게도 update가 된다.
button을 클릭하면 PlusButton 의 onClick에 의해 CounterProvider의 state가 변경되면서 CountProvider가 re render된다.
이때 Producer도 re render된다.
이때 Provider의 하위 컴포넌트도 다 rerender가 된다.
Provider componentDidUpdate가 호출되면서 emitter set을 호출해서 등록된 handler들을 호출한다. 이때 handler는 setValue로서 Consumer의 state가 변경이된다.
즉 context의 provider에 value로 넣어준 값(state, setState)을 통해서 같은 context의 consumer들이 onClick을 통해provider의 state를 setState로 넣어준 값을 바뀌게 하면 Provider -> Conusmer re rendering이 된다.
컨텍스트가 해결할 수 있는 문제
- 인접한 컴포넌트 간에만 인자를 전달할 수 있는 구조적 한계
- 중간 컴포넌트 없이 비교적 직접 전달할 수 있는 방법이 리액트 컨택스트
'FrontEnd > [리액트 2부] 고급 주제와 훅' 카테고리의 다른 글
[3.1장 클래스/함수 컴포넌트][3.2장 상태훅] (0) | 2024.03.11 |
---|---|
[2.4장 다이얼로그1][2.5장 다이얼로그2] (0) | 2024.03.07 |
[2.2장 라우터 1] [2.3장 라우터 2] (0) | 2024.03.04 |
[1.4장 장바구니 화면] (0) | 2024.02.27 |
[1.2장 상품목록 화면] [1.3장 주문내역 화면] (0) | 2024.02.22 |