๋ฐ˜์‘ํ˜•

TL;DR


๋ฆฌ์•กํŠธ์—์„œ ์ œ๊ณตํ•˜๋Š” ContextAPI๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์›ํ•˜๋Š” ์ƒํƒœ(๊ฐ’)๋ฅผ ์ „์—ญ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ๋ผ๋ฆฌ ํŠน์ • ์ƒํƒœ๋ฅผ ๊ณต์œ ํ•  ๋•Œ๋„ ์œ ์šฉํ•˜๋‹ค. ํ•˜์ง€๋งŒ Context.Provider ํ•˜์œ„์—์„œ ์ปจํ…์ŠคํŠธ๋ฅผ ๊ตฌ๋…ํ•˜๋Š” ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋Š” ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ๋•Œ๋งˆ๋‹ค ๋ฆฌ๋ Œ๋”๋ง ๋œ๋‹ค. Context.Provider ๊ฐ’์ด ์—…๋ฐ์ดํŠธ๋˜๋ฉด useContext ํ›…์ด ์ตœ์‹  ์ปจํ…์ŠคํŠธ ๊ฐ’์„ ์‚ฌ์šฉํ•ด ์ปดํฌ๋„ŒํŠธ ๋ฆฌ๋ Œ๋”๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

์ด์ฒ˜๋Ÿผ ContextAPI๋Š” ๋ Œ๋”๋ง ์ตœ์ ํ™”์— ์ผ์ผ์ด ์‹ ๊ฒฝ์จ์•ผํ•˜๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค. ์—ฌ๋Ÿฌ ์ตœ์ ํ™” ๋ฐฉ๋ฒ•์ค‘์—์„œ ์ƒํƒœ/๋””์ŠคํŒจ์น˜ ๋‹จ์œ„๋กœ ์ปจํ…์ŠคํŠธ๋ฅผ ์ชผ๊ฐœ๋Š” ๋ฐฉ์‹์ด ๊ฐ€์žฅ ๊ฐ„๋‹จํ•˜๋‹ค. ๋Œ€์‹  Provider๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์•„์ง€๋Š” ๋‹จ์ (Wrapper Hell)์ด ์žˆ๋‹ค.

 

  • Context ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ํ•ด๋‹น Context.Provider ์•ˆ์ชฝ์—์„œ ์ƒํƒœ๋ฅผ ๊ตฌ๋…(์†Œ๋น„)ํ•˜๋Š” ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง ๋œ๋‹ค
  • ์ƒํƒœ(state)์™€ ๋””์ŠคํŒจ์น˜(dispatch) ํ•จ์ˆ˜๋ฅผ ๋™์ผํ•œ Context์— ๋„ฃ์œผ๋ฉด, ๋””์ŠคํŒจ์น˜ ํ•จ์ˆ˜๋งŒ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋„ ์ƒํƒœ ์—…๋ฐ์ดํŠธ์‹œ ๋ฆฌ๋ Œ๋”๋ง ๋œ๋‹ค โšก๏ธ
  • ์ƒํƒœ/๋””์ŠคํŒจ์น˜ ํ•จ์ˆ˜ ์ „์šฉ Context๋ฅผ ๊ฐ๊ฐ ๋งŒ๋“ค๋ฉด ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค
  • ์ƒํƒœ/๋””์ŠคํŒจ์น˜ ํ•จ์ˆ˜๋ฅผ ๊ฐ™์€ Context์— ๋„ฃ์–ด์•ผ ํ•œ๋‹ค๋ฉด useMemo๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค โšก๏ธ
  • ์ด์™ธ์—๋„ React.memo๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ปจํ…์ŠคํŠธ ๊ฐ’์„ prop์œผ๋กœ ๋ฐ›๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค๊ฑฐ๋‚˜, use-context-selector ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋“ฑ์„ ์‚ฌ์šฉํ•ด์„œ ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค
  • Provider ์•ˆ์ชฝ์— ์žˆ์ง€๋งŒ ์ปจํ…์ŠคํŠธ ์ƒํƒœ๋ฅผ ๊ตฌ๋…ํ•˜์ง€ ์•Š๋Š” ์ปดํฌ๋„ŒํŠธ๋Š” ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋ผ๋„ ๋ฆฌ๋ Œ๋”๋ง ๋˜์ง€ ์•Š๋Š”๋‹ค

 

๋ฆฌ๋ Œ๋”๋ง ๋ฐœ์ƒ ์ƒํ™ฉ


FamilyName ํผ๋งŒ ๋ณ€๊ฒฝํ–ˆ๋Š”๋ฐ FirstName ํผ๋„ ๊ฐ™์ด ๋ฆฌ๋ Œ๋”๋ง๋˜๊ณ  ์žˆ๋‹ค

FamilyName, FirstName 2๊ฐœ์˜ input์ด ์žˆ๊ณ , ์ปจํ…์ŠคํŠธ ์—ญ์‹œ 2๊ฐœ๋ฅผ ๋งŒ๋“ค์–ด์„œ ๊ฐ input์˜ ์ƒํƒœ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค. ์ด๋ ‡๊ฒŒ form ๋ฐ์ดํ„ฐ๋ฅผ ์ปดํฌ๋„ŒํŠธ ์ƒํƒœ๋กœ ๋‘๋Š” ๋ฐฉ์‹์„ controlled form๋ผ๊ณ  ํ•œ๋‹ค(๋ฐ˜๋Œ€๋กœ value ๋ณ€๊ฒฝ์„ ๊ตฌ๋…ํ•˜์ง€ ์•Š๊ณ  ํ•„์š”ํ•  ๋•Œ๋งŒ pullํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์„ uncontrolled๋ผ๊ณ  ํ•œ๋‹ค).

 

FamilyName, FirstName 2๊ฐœ์˜ ์ปจํ…์ŠคํŠธ๋ฅผ ๋งŒ๋“ค์—ˆ์ง€๋งŒ, ๋‘˜ ์ค‘ 1๊ฐœ ์ปจํ…์ŠคํŠธ์˜ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด Provider ์•ˆ์ชฝ์—์„œ ์ƒํƒœ๋ฅผ ๊ตฌ๋…ํ•˜๋Š” ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง ๋œ๋‹ค.

๋”๋ณด๊ธฐ

`Context.Provider` ์•ˆ์—์„œ `useContext` ์‚ฌ์šฉ์„ ๊ฐ•์ œํ•˜๊ธฐ ์œ„ํ•œ ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜ โ–ผ

import { useContext } from 'react';

// ์ปจํ…์ŠคํŠธ Provider ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” factory ํ•จ์ˆ˜
export const factoryUseContext =
  (context, name = '') =>
  () => {
    const ctx = useContext(context);

    switch (ctx) {
      case undefined:
      case null:
        const ctxName = name === '' ? '' : `${name} `;
        const msg = `${ctxName}Context must be used withing a ${ctxName}ContextProvider`;
        throw new Error(msg);
      default:
        return ctx;
    }
  };
// context/NameContext1
import { factoryUseContext } from './factoryUseContext';

const FamilyNameContext = createContext(null);
const FirstNameContext = createContext(null);

const useFamilyNameCtx = factoryUseContext(FamilyNameContext);
const useFirstNameCtx = factoryUseContext(FirstNameContext);

const Provider = ({ children }) => {
  const [familyName, setFamilyName] = useState(''); // familyName input์— ์‚ฌ์šฉ
  const [firstName, setFirstName] = useState(''); // firstName input์— ์‚ฌ์šฉ

  return (
    <FamilyNameContext.Provider value={[familyName, setFamilyName]}>
      <FirstNameContext.Provider value={[firstName, setFirstName]}>
        {children}
      </FirstNameContext.Provider>
    </FamilyNameContext.Provider>
  );
};
// context/useNameContext.js
// ...

export const useNameContext = (selected) => {
  const { Context } = contextMap[selected];
  return useCallback(
    () => (
      <Context.Provider>
        <NameInput useContext={Context.useFamilyNameCtx} name="FamilyName" />
        <NameInput useContext={Context.useFirstNameCtx} name="FirstName" />
        <Dummy />{' '}
        {/* Provider ์•ˆ์ชฝ์— ์žˆ์ง€๋งŒ ์ปจํ…์ŠคํŠธ ๊ฐ’์„ ๊ตฌ๋…ํ•˜์ง€ ์•Š์•„์„œ ๋ฆฌ๋ Œ๋”๋ง ์•ˆํ•จ */}
      </Context.Provider>
    ),
    [Context],
  );
};
๋”๋ณด๊ธฐ
import {
  NameContext1,
  NameContext2,
  NameContext3,
  NameContext4,
  NameContext5,
} from './';
import { Dummy, NameInput } from '../components';
import { useCallback } from 'react';

const contextMap = {
  context1: {
    Context: NameContext1,
    label: 'CONTEXT_1 (only state ctx)',
  },
  context2: {
    Context: NameContext2,
    label: 'CONTEXT_2 (state/dispatch ctx)',
  },
  context3: {
    Context: NameContext3,
    label: 'CONTEXT_3 (only state ctx with memo)',
  },
  context4: {
    Context: NameContext4,
    label: 'CONTEXT_4 (state/dispatch ctx with reducer)',
  },
  context5: {
    Context: NameContext5,
    label: 'CONTEXT_5 (only state ctx with reducer/memo)',
  },
};

export const options = Object.entries(contextMap).map(([k, v]) => ({
  value: k,
  label: v.label,
}));

export const useNameContext = (selected) => {
  const { Context } = contextMap[selected];
  return useCallback(
    () => (
      <Context.Provider>
        <NameInput useContext={Context.useFamilyNameCtx} name="FamilyName" />
        <NameInput useContext={Context.useFirstNameCtx} name="FirstName" />
        <Dummy />
      </Context.Provider>
    ),
    [Context],
  );
};
// index.js
import { options, useNameContext } from './context';
const [, secondOption] = options;

function App() {
  // ...
  const NameContextProvider = useNameContext(selectedCtx);

  // ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ ์ƒ๋žต
  return <NameContextProvider />;
}
๋”๋ณด๊ธฐ
import React, { useState, useCallback } from 'react';
import ReactDOM from 'react-dom/client';
import style from './style.css';
import { SelectBox } from './components';
import { options, useNameContext } from './context';

const [, secondOption] = options;

function App() {
  const [selectedCtx, setSelectedCtx] = useState(secondOption.value);
  const onChange = useCallback((value) => setSelectedCtx(value), []);
  const NameContextProvider = useNameContext(selectedCtx);

  return (
    <>
      <h1 className="title">Context API Rendering Test</h1>
      <div className="mb-7">
        <span className="span-label">Selected Context</span>
        <SelectBox
          options={options}
          onChange={onChange}
          deaultValue={selectedCtx}
        />
      </div>
      <NameContextProvider />
    </>
  );
}

const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<App />);
// components/NameInput.js
const NameInput = ({ useContext, name }) => {
  const [state, setState] = useContext();
  // ...

  return (
    <div className="mb-10">
      {/* ... */}
      <input
        name={name}
        type="text"
        value={state}
        onChange={({ target }) => setState(target.value)}
        placeholder="input value then check number of rendering times"
      />
    </div>
  );
};
๋”๋ณด๊ธฐ
import React, { useRef } from 'react';

const NameInput = ({ useContext, name }) => {
  const [state, setState] = useContext();
  const count = useRef(0);

  return (
    <div className="mb-10">
      <span className="span-label">
        <span>{`${name} Rendered`}</span>
        <strong className="counter">{`${++count.current}`}</strong>
      </span>
      <input
        name={name}
        type="text"
        value={state}
        onChange={({ target }) => setState(target.value)}
        placeholder="input value then check number of rendering times"
      />
    </div>
  );
};

export default NameInput;

 

๋ Œ๋”๋ง ์ตœ์ ํ™”


์ƒํƒœ / ๋””์ŠคํŒจ์น˜ ์ปจํ…์ŠคํŠธ ๋ถ„๋ฆฌํ•ด์„œ ๋ฆฌ๋ Œ๋”๋ง ๋ฐฉ์ง€

FamilyName ํผ๋งŒ ๋ฆฌ๋ Œ๋”๋ง๋œ ํ™”๋ฉด

๐Ÿ’ก Provider๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์•„์ง€๋Š” ๋‹จ์ ์ด ์žˆ์ง€๋งŒ(Wrapper Hell) ์ œ์ผ ๋ฌด๋‚œํ•œ ๋ฐฉ๋ฒ•

 

์ƒํƒœ(state) ์ „์šฉ Context์™€ ๋””์ŠคํŒจ์น˜(dispatch) ์ „์šฉ Context๋ฅผ ๊ฐ๊ฐ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค. FamilyName, FirstName 2๊ฐœ์˜ ์ƒํƒœ๋กœ ๊ด€๋ฆฌํ•˜๋ฏ€๋กœ ์ด 4๊ฐœ์˜ ์ปจํ…์ŠคํŠธ๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค.

  1. FamilyName
    • FamilyNameStateContext
    • FamilyNameDispatchContext
  2. FirstName
    • FirstNameStateContext
    • FirstNameDispatchContext

 

// context/NameContext2
const FamilyNameStateContext = createContext(null);
const FamilyNameDispatchContext = createContext(null);
const FirstNameStateContext = createContext(null);
const FirstNameDispatchContext = createContext(null);

// use...Context ํ›… ์ƒ๋žต

const Provider = ({ children }) => {
  const [familyName, setFamilyName] = useState('');
  const [firstName, setFirstName] = useState('');

  return (
    <FamilyNameStateContext.Provider value={familyName}>
      <FamilyNameDispatchContext.Provider value={setFamilyName}>
        <FirstNameStateContext.Provider value={firstName}>
          <FirstNameDispatchContext.Provider value={setFirstName}>
            {children}
          </FirstNameDispatchContext.Provider>
        </FirstNameStateContext.Provider>
      </FamilyNameDispatchContext.Provider>
    </FamilyNameStateContext.Provider>
  );
};
๋”๋ณด๊ธฐ
import React, { useState, createContext } from 'react';
import { factoryUseContext } from './factoryUseContext';

// state, dispatch ์ปจํ…์ŠคํŠธ๋ฅผ ๋ถ„๋ฆฌํ•ด์„œ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ•
const FamilyNameStateContext = createContext(null);
const FamilyNameDispatchContext = createContext(null);
const FirstNameStateContext = createContext(null);
const FirstNameDispatchContext = createContext(null);

const useFamilyNameCtx = () => [
  factoryUseContext(FamilyNameStateContext)(),
  factoryUseContext(FamilyNameDispatchContext)(),
];

const useFirstNameCtx = () => [
  factoryUseContext(FirstNameStateContext)(),
  factoryUseContext(FirstNameDispatchContext)(),
];

const Provider = ({ children }) => {
  const [familyName, setFamilyName] = useState('');
  const [firstName, setFirstName] = useState('');

  return (
    <FamilyNameStateContext.Provider value={familyName}>
      <FamilyNameDispatchContext.Provider value={setFamilyName}>
        <FirstNameStateContext.Provider value={firstName}>
          <FirstNameDispatchContext.Provider value={setFirstName}>
            {children}
          </FirstNameDispatchContext.Provider>
        </FirstNameStateContext.Provider>
      </FamilyNameDispatchContext.Provider>
    </FamilyNameStateContext.Provider>
  );
};

export { useFamilyNameCtx, useFirstNameCtx, Provider };

 

useMemo ํ™œ์šฉ ๋ฆฌ๋ Œ๋”๋ง ๋ฐฉ์ง€

FamilyName ํผ๋งŒ ๋ฆฌ๋ Œ๋”๋ง๋œ ํ™”๋ฉด

์ƒํƒœ(state)์™€ ๋””์ŠคํŒจ์น˜(disatch) ํ•จ์ˆ˜๋ฅผ ๋™์ผํ•œ Context์— ๋„ฃ์–ด์•ผ ํ•œ๋‹ค๋ฉด useMemo๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค. dispatch ํ•จ์ˆ˜๋Š” ํ•œ๋ฒˆ ์ •์˜ํ•˜๋ฉด ๋ณ€๊ฒฝ์ด ์—†์œผ๋ฏ€๋กœ useMemo ์˜์กด์„ฑ ๋ฐฐ์—ด์—” ์ƒํƒœ(state)๋งŒ ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค.

// context/NameContext3
// createContext ์ƒ๋žต
// use...Context ํ›… ์ƒ๋žต

const Provider = ({ children }) => {
  const [familyName, setFamilyName] = useState('');
  const [firstName, setFirstName] = useState('');

  const familyNameValue = useMemo(
    () => [familyName, setFamilyName],
    [familyName],
  );
  const firstNameValue = useMemo(() => [firstName, setFirstName], [firstName]);

  return (
    <FamilyNameContext.Provider value={familyNameValue}>
      <FirstNameContext.Provider value={firstNameValue}>
        {children}
      </FirstNameContext.Provider>
    </FamilyNameContext.Provider>
  );
};
๋”๋ณด๊ธฐ
import React, { useState, createContext, useMemo } from 'react';
import { factoryUseContext } from './factoryUseContext';

// useMemo๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ•
const FamilyNameContext = createContext(null);
const FirstNameContext = createContext(null);

const useFamilyNameCtx = factoryUseContext(FamilyNameContext);
const useFirstNameCtx = factoryUseContext(FirstNameContext);

const Provider = ({ children }) => {
  const [familyName, setFamilyName] = useState('');
  const [firstName, setFirstName] = useState('');

  const familyNameValue = useMemo(
    () => [familyName, setFamilyName],
    [familyName],
  );
  const firstNameValue = useMemo(() => [firstName, setFirstName], [firstName]);

  return (
    <FamilyNameContext.Provider value={familyNameValue}>
      <FirstNameContext.Provider value={firstNameValue}>
        {children}
      </FirstNameContext.Provider>
    </FamilyNameContext.Provider>
  );
};

export { useFamilyNameCtx, useFirstNameCtx, Provider };

 

useReducer๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋ฆฌ๋ Œ๋”๋ง ๋ฐฉ์ง€

FamilyName ํผ๋งŒ ๋ฆฌ๋ Œ๋”๋ง๋œ ํ™”๋ฉด

useReducer๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ์˜ ๋ Œ๋”๋ง ์ตœ์ ํ™” ์—ญ์‹œ โžŠ์ƒํƒœ/๋””์ŠคํŒจ์น˜ Context๋ฅผ ์ชผ๊ฐœ๊ฑฐ๋‚˜, โž‹useMemo(๋””์ŠคํŒจ์น˜ Context๋ฅผ ๋งŒ๋“ค์ง€ ์•Š์„๋•Œ)๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. ์•„๋ž˜๋Š” 1๊ฐœ reducer๋งŒ ์‚ฌ์šฉํ•ด์„œ FamilyName, FirstName์— ํ•ด๋‹นํ•˜๋Š” ์ƒํƒœ ๊ฐ’์„ ์ปจํ…์ŠคํŠธ value๋กœ ๋„ฃ์–ด์„œ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ์‹œ(์ƒํƒœ/๋””์ŠคํŒจ์น˜ Context ๋ถ„๋ฆฌ).

 

๐Ÿ’ก 1๊ฐœ reducer์˜ ๋””์ŠคํŒจ์น˜ ํ•จ์ˆ˜๋ฅผ ์—ฌ๋Ÿฌ ์ปจํ…์ŠคํŠธ์—์„œ ์‚ฌ์šฉํ•  ๋• ๋””์ŠคํŒจ์น˜ ํ•จ์ˆ˜๋ฅผ useCallback์œผ๋กœ ํ•œ ๋ฒˆ ๊ฐ์‹ธ์ค˜์•ผ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

// context/NameContext4
// createContext ์ƒ๋žต

const SET_FAMILY_NAME = 'SET_FAMILY_NAME';
const SET_FIRST_NAME = 'SET_FIRST_NAME';
const familyNameAction = (payload) => ({ type: SET_FAMILY_NAME, payload });
const firstNameAction = (payload) => ({ type: SET_FIRST_NAME, payload });

const reducer = (state, action) => {
  switch (action.type) {
    case SET_FAMILY_NAME:
      return { ...state, familyName: action.payload };
    case SET_FIRST_NAME:
      return { ...state, firstName: action.payload };
    default:
      throw new Error(`Unhandled action type: ${action.type}`);
  }
};

// use...Context ํ›… ์ƒ๋žต

const initialState = { familyName: '', firstName: '' };

const Provider = ({ children }) => {
  const [{ familyName, firstName }, dispatch] = useReducer(
    reducer,
    initialState,
  );

  // 1๊ฐœ reducer์˜ ๋””์ŠคํŒจ์น˜ ํ•จ์ˆ˜๋ฅผ ์—ฌ๋Ÿฌ ์ปจํ…์ŠคํŠธ์—์„œ ์‚ฌ์šฉํ•  ๋• useCallback์œผ๋กœ ๊ฐ์‹ธ์ค˜์•ผ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค
  const setFamilyName = useCallback((v) => dispatch(familyNameAction(v)), []);
  const setFirstName = useCallback((v) => dispatch(firstNameAction(v)), []);

  return (
    <FamilyNameStateContext.Provider value={familyName}>
      <FamilyNameDispatchContext.Provider value={setFamilyName}>
        <FirstNameStateContext.Provider value={firstName}>
          <FirstNameDispatchContext.Provider value={setFirstName}>
            {children}
          </FirstNameDispatchContext.Provider>
        </FirstNameStateContext.Provider>
      </FamilyNameDispatchContext.Provider>
    </FamilyNameStateContext.Provider>
  );
};
๋”๋ณด๊ธฐ
import React, { createContext, useReducer, useCallback } from 'react';
import { factoryUseContext } from './factoryUseContext';

// useReducer ์‚ฌ์šฉ, state/dispatch ์ปจํ…์ŠคํŠธ ๋ถ„๋ฆฌํ•ด์„œ ๋ฆฌ๋ Œ๋”๋ง ๋ฐฉ์ง€
const FamilyNameStateContext = createContext(null);
const FamilyNameDispatchContext = createContext(null);
const FirstNameStateContext = createContext(null);
const FirstNameDispatchContext = createContext(null);

const SET_FAMILY_NAME = 'SET_FAMILY_NAME';
const SET_FIRST_NAME = 'SET_FIRST_NAME';
const familyNameAction = (payload) => ({ type: SET_FAMILY_NAME, payload });
const firstNameAction = (payload) => ({ type: SET_FIRST_NAME, payload });

const reducer = (state, action) => {
  switch (action.type) {
    case SET_FAMILY_NAME:
      return { ...state, familyName: action.payload };
    case SET_FIRST_NAME:
      return { ...state, firstName: action.payload };
    default:
      throw new Error(`Unhandled action type: ${action.type}`);
  }
};

const useFamilyNameCtx = () => [
  factoryUseContext(FamilyNameStateContext)(),
  factoryUseContext(FamilyNameDispatchContext)(),
];

const useFirstNameCtx = () => [
  factoryUseContext(FirstNameStateContext)(),
  factoryUseContext(FirstNameDispatchContext)(),
];

const initialState = { familyName: '', firstName: '' };

const Provider = ({ children }) => {
  const [{ familyName, firstName }, dispatch] = useReducer(
    reducer,
    initialState,
  );

  // 1๊ฐœ reducer๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ useCallback์œผ๋กœ dispatch๋ฅผ ๊ฐ์‹ธ์ค˜์•ผ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค
  // State, Dispatch ์ปจํ…์ŠคํŠธ๋ฅผ ๋ถ„๋ฆฌํ•˜์ง€ ์•Š์œผ๋ฉด ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•œ๋‹ค
  const setFamilyName = useCallback((v) => dispatch(familyNameAction(v)), []);
  const setFirstName = useCallback((v) => dispatch(firstNameAction(v)), []);

  return (
    <FamilyNameStateContext.Provider value={familyName}>
      <FamilyNameDispatchContext.Provider value={setFamilyName}>
        <FirstNameStateContext.Provider value={firstName}>
          <FirstNameDispatchContext.Provider value={setFirstName}>
            {children}
          </FirstNameDispatchContext.Provider>
        </FirstNameStateContext.Provider>
      </FamilyNameDispatchContext.Provider>
    </FamilyNameStateContext.Provider>
  );
};

export { useFamilyNameCtx, useFirstNameCtx, Provider };

 

CodeSandbox


 

 

์ฐธ๊ณ  ๊ธ€


 


๊ธ€ ์ˆ˜์ •์‚ฌํ•ญ์€ ๋…ธ์…˜ ํŽ˜์ด์ง€์— ๊ฐ€์žฅ ๋น ๋ฅด๊ฒŒ ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”
๋ฐ˜์‘ํ˜•