function createContext(initialValue) {
const emitter = createEventEmitter(initialValue);
function Provider({ value, children }) {
React.useEffect(() => {
emitter.set(value);
}, [value]);
return <>{children}</>;
}
return {
Provider,
emitter,
};
}
function useContext(context) {
const [value, setValue] = React.useState(context.emitter.get());
React.useEffect(() => {
context.emitter.on(setValue);
return () => context.emitter.off(setValue);
}, [context]);
return value;
}
createContext에서 init value가 지정된 emitter를 생성한다.
Provider에서는 넘겨받은 value가 변화가 생기면 emitter 에 등록된 callback들을 변화된value를 인자로
주어 호출하게 된다.
(즉 초기에 넘겨받은 value를 Conusmer가 쓸 수 있도록 set 해주는 역할이다.)
useContext 즉 Consumer 역할이다. emitter의 value값에 대하여 setValue 함수를 callback으로 emitter에게 등록을 한다.
const countContext = MyReact.createContext({});
const CounterProvider = ({ children }) => {
const [count, setCount] = React.useState(0);
const value = { count, setCount };
return (
<countContext.Provider value={value}>{children}</countContext.Provider>
);
};
const Count = () => {
const { count } = MyReact.useContext(countContext);
return <div>{count}</div>;
};
const PlusButton = () => {
const { count, setCount } = MyReact.useContext(countContext);
const handleClick = () => {
setCount(count+1);
};
return <button onClick={handleClick}>count plus</button>;
};
export default () => (
<CounterProvider>
<Count></Count>
<PlusButton></PlusButton>
</CounterProvider>
);
CounterProvider가 countContext Provider를 하위 component에게 제공한다.
이를 Consume하는 Count, PlustButton은 useContxt를 통해 Provider에서 전달해준 count, setCount를 사용하게 된다.
Count는 count prop을 render , PlusButton은 setCount를 통해 값을 증가시킨다.
첫 context.emitter.get에서는 createContext에서 주어진 {} 빈객체가 들어가 있는것을 확인 할 수 있다.
이후 Consumer useEffect에서 emitter.on을 통해 useState를 빈 객체로 초기화하고 return으로 나온 setValue를 등록한다.
Provider useEffect에서 emitter.set을 통해 CountProvider에서 전달받은 value를 consumer에게 제대로 set하게된다.
이때 왜 state도 없는 Count, PlusButton이 render가 다시될까? 조건이 뭘까?
custom hook의 조건은 내부에서 react hook을 쓰기만 하면 된다.
export const useNavigate = () => {
const { path, changePath } = useContext(routerContext);
const navigate = (nextPath) => {
if (path !== nextPath) changePath(nextPath);
};
return navigate;
};
// const navigate = useNavigate()
// navigate("/cart")
export const useMatch = () => {
const { path } = useContext(routerContext);
const match = (comparedPath) => path === comparedPath;
return match;
};
export const useParams = () => {
const params = () => {
const params = new URLSearchParams(window.location.search);
const paramObject = {};
for (const [key, value] of params) {
paramObject[key] = value;
}
return paramObject;
};
return params
};
useParams는 내부에서 react hook을 사용하지 않기 때문에 custom hook이 아니다.
기존의 고차 컴포넌트를 함수 컴포넌트에서 사용하기 편하게 변경한것이다.
export const withRouter = (WrappedComponent) => {
const WithRouter = (props) => (
<routerContext.Consumer>
{({ path, changePath }) => {
const navigate = (nextPath) => {
if (path !== nextPath) changePath(nextPath);
};
const match = (comparedPath) => path === comparedPath;
const params = () => {
const params = new URLSearchParams(window.location.search);
const paramObject = {};
for (const [key, value] of params) {
paramObject[key] = value;
}
return paramObject;
};
const enhancedProps = {
navigate,
match,
params,
};
return <WrappedComponent {...props} {...enhancedProps} />;
}}
</routerContext.Consumer>
);
WithRouter.displayName = `WithRouter(${getComponentName(WrappedComponent)})`;
return WithRouter;
};
'FrontEnd > [리액트 2부] 고급 주제와 훅' 카테고리의 다른 글
[4.3 리듀서 훅] (0) | 2024.03.22 |
---|---|
[4.1장 레프 훅][4.2장 레프 훅] (0) | 2024.03.19 |
[3.2장 상태 훅] (0) | 2024.03.12 |
[3.1장 클래스/함수 컴포넌트][3.2장 상태훅] (0) | 2024.03.11 |
[2.4장 다이얼로그1][2.5장 다이얼로그2] (0) | 2024.03.07 |