원글 페이지 : 바로가기
👍 참고 1. https://solo5star.dev/posts/42/ 2. https://velog.io/@velopert/react-context-tutorial#%EA%B0%92%EA%B3%BC-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8-%ED%95%A8%EC%88%98%EB%A5%BC-%EB%91%90%EA%B0%9C%EC%9D%98-context%EB%A1%9C-%EB%B6%84%EB%A6%AC%ED%95%98%EA%B8%B0 3. https://velog.io/@dahyeon405/Context-API%EC%9D%98-%EB%A6%AC%EB%A0%8C%EB%8D%94%EB%A7%81-%EB%8C%80%ED%95%9C-%EC%98%A4%ED%95%B4 👀 Provider 내부에 컴포넌트가 있는 경우 (
const CounterActionContext = createContext({
increase: () => {},
decrease: () => {},
});
export function CounterProvider({ children }: { children: React.ReactNode }) {
const [counter, setCounter] = useState(0);
const increase = () => setCounter((prev) => prev + 1);
const decrease = () => setCounter((prev) => prev – 1);
console.log(‘Counter Provider’);
return (
{children}
);
}
export function useCounter() {
const value = useContext(CounterValueContext);
return value;
}
export function useSetCounter() {
const value = useContext(CounterActionContext);
return value;
} export default function ContextPage() {
console.log(‘Context Page’);
return (
);
} // Text.tsx
export default function Text() {
console.log(‘Text’);
return
Counter Text
;
}
// Plus.tsx
export default function Plus() {
const { increase } = useSetCounter();
console.log(‘Plus’);
return ;
}
// Minus.tsx
export default function Minus() {
const { decrease } = useSetCounter();
console.log(‘Minus’);
return ;
}
// Number.tsx
export default function Number() {
const count = useCounter();
console.log(‘Number’);
return
;
}
// Hello.tsx
export default function Hello() {
console.log(‘Hello’);
return
;
}
const [counter, setCounter] = useState(0);
const actions = useMemo(
() => ({
increase() {
setCounter((prev) => prev + 1);
},
decrease() {
setCounter((prev) => prev – 1);
},
}),
[],
);
console.log(‘Counter Provider’);
return (
{children}
);
} increase, decrease 각 함수에 useCallback을 적용해 value = {{ increase, decrease }} 로 전달한 경우, 렌더링 최적화가 적용되지 않는다. 왜냐하면
const [counter, setCounter] = useState(0);
const actions = useMemo(
() => ({
increase() {
setCounter((prev) => prev + 1);
},
decrease() {
setCounter((prev) => prev – 1);
},
}),
[],
);
console.log(‘Counter Provider’);
return (
{children}
);
} export default function ContextPage() {
console.log(‘Context Page’);
return (
);
} // Text.tsx
export default function Text({ children }: { children: React.ReactNode }) {
console.log(‘Text’);
return (
Counter Text
{children}
);
}
// HeadingNumber.tsx
export default function HeadingNumber() {
const count = useCounter();
console.log(‘Heading Number’);
return ★ {count} ★;
}
// Hello.tsx
export default function Hello() {
console.log(‘Hello’);
const count = useCounter();
return
;
} –
counter: 0,
actions: { increase: () => {}, decrease: () => {} },
});
export function CounterProvider({ children }: { children: React.ReactNode }) {
const [counter, setCounter] = useState(0);
const actions = useMemo(
() => ({
increase() {
setCounter((prev) => prev + 1);
},
decrease() {
setCounter((prev) => prev – 1);
},
}),
[],
);
const value = useMemo(() => ({ counter, actions }), [counter, actions]);
console.log(‘Counter Provider’);
return (
);
}
export function useCounter() {
const value = useContext(CounterContext);
return value;
} // Plus.tsx
export default function Plus() {
const {
actions: { increase },
} = useCounter();
console.log(‘Plus’);
return ;
}
// Minus.tsx
export default function Minus() {
const {
actions: { decrease },
} = useCounter();
console.log(‘Minus’);
return ;
} 하나의 provider를 통해 값을 전달하고 있는 경우 context의 값을 사용하고 있는 모든 컴포넌트에서 리렌더링이 발생한다. actions는 useMemo를 적용했어도, counter가 변경될 때마다 value의 useMemo dependencies가 바뀌기 때문이다. 그래서 상태가 자주 변경되는 경우에는 값과 상태 변경 함수 provider를 분리하여 관리하는게 좋다.