๋ฐ˜์‘ํ˜•

children Prop์„ ์ด์šฉํ•ด ์ปดํฌ๋„ŒํŠธ ํ•ฉ์„ฑ(component composition) ํ˜•ํƒœ๋กœ ์ž‘์„ฑํ•˜๋ฉด ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ ๋„ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž์œ ๋กญ๊ฒŒ ์ถ”๊ฐ€ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์ผ๋ฐ˜์ ์œผ๋กœ ๋ฐ์ดํ„ฐ(์ƒํƒœ) ๊ณต์œ ๋Š” ์ƒํƒœ ๋Œ์–ด์˜ฌ๋ฆฌ๊ธฐ ๋ฐฉ๋ฒ•์ด ๊ฐ€์žฅ ๊ฐ„๋‹จํ•˜์ง€๋งŒ ์ตœ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฐ์ดํ„ฐ(์ƒํƒœ)์™€ ๋ชจ๋“  ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ–๊ณ  ์žˆ์œผ๋ฉด ํ•ด๋‹น ์ƒํƒœ์™€ ๊ด€๋ จ ์—†๋Š” ๋ชจ๋“  ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋„ ๋ Œ๋”๋ง์— ์˜ํ–ฅ์„ ์ฃผ๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค(์ƒํƒœ ๋Œ์–ด์˜ฌ๋ฆฌ๊ธฐ๋กœ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋ผ์„œ ์—…๋ฐ์ดํŠธํ•˜๋ฉด ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋„ ๋‹ค์‹œ ๋ Œ๋”๋ง ํ•˜๋ฏ€๋กœ).

 

์ด๋• Render Props ํŒจํ„ด์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ• ์ง€์— ๋Œ€ํ•œ ๋กœ์ง์„ ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฒฐ์ •ํ•˜๊ณ , ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋Š” ์ž์‹ ์ด ๊ด€๋ฆฌํ•˜๋Š” ์ƒํƒœ๋ฅผ ์ธ์ž๋กœ ๋ฐ›์€ render(ํ˜น์€ children) Prop์„ ํ˜ธ์ถœํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋‚˜ ๋กœ์ง์„ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—๊ฒŒ ๊ณต์œ ํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค.

๋”๋ณด๊ธฐ

์ƒํƒœ ๋Œ์–ด์˜ฌ๋ฆฌ๊ธฐ ์˜ˆ์‹œ. ์ž…๋ ฅ ์ƒํƒœ๋Š” App์ด ๊ฐ€์ง€๊ณ  ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์ธ Input์—์„œ ์ƒํƒœ ๋Œ์–ด์˜ฌ๋ฆฌ๊ธฐ๋กœ ์ž…๋ ฅ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋ฉด ๋‹ค๋ฅธ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋„ ๋ชจ๋‘ ๋‹ค์‹œ ๋ Œ๋”๋งํ•œ๋‹ค.

 

์ด๋ฏธ์ง€ ์ถœ์ฒ˜ - study-ihl
function Input({ value, handleChange }) {
  return <input value={value} onChange={(e) => handleChange(e.target.value)} />;
}

function App() {
  const [value, setValue] = useState(''); // value๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ๋ชจ๋“  ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋„ ๋‹ค์‹œ ๋ Œ๋”๋ง

  return (
    <div className="App">
      <h1>Temperature Converter</h1>
      <Input value={value} handleChange={setValue} />
      <Kelvin value={value} />
      <Fahreheit value={value} />
    </div>
  );
}

 

Render Props ํŒจํ„ด ์˜ˆ์‹œ. App ์ปดํฌ๋„ŒํŠธ ์ž์‹์œผ๋ก  Input๋งŒ ๊ฐ€์ง€๊ณ , Input ์ž์‹์— ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ• ์ง€๋Š” App ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฒฐ์ •. ์ž…๋ ฅ ์ƒํƒœ๋Š” Input์ด ๊ด€๋ฆฌํ•˜๊ณ , Input ๋‚ด๋ถ€ ์ƒํƒœ๋Š” Render Prop ํ˜•ํƒœ๋กœ ๋‹ค์‹œ Input ์ž์‹์œผ๋กœ ์ „๋‹ฌ.

 

์ด๋ฏธ์ง€ ์ถœ์ฒ˜ - study-ihl
function App() {
  return (
    <div className="App">
      <h1>Temperature Converter</h1>
      <input
        render={(value) => (
          <>
            <Kelvin value={value} />
            <Fahrenheit value={value} />
          </>
        )}
      />
    </div>
  );
}
function Input({ render }) {
  const [value, setValue] = useState('');

  return (
    <>
      <input
        type="text"
        value={value}
        onChange={(e) => setValue(e.target.value)}
        placeholder="Temp in โ„ƒ"
      />
      {render(value)} {/* value๋Š” Kelvin, Fahrenheit ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌ๋จ */}
    </>
  );
}

 

๋ฐฉ๋ฒ• 1 — Children Prop


children Props ํ˜•ํƒœ๋กœ ์‚ฌ์šฉ

 

export default function CheckoutMobile({ orders, initialOrderIds }): Props {
  const {
    // ...
    getTableProps, // Native Props ๋…ธ์ถœ๋Œ€์‹  Props Getters ๋ชฉ๋ก์„ ์ œ๊ณตํ•˜๋Š” Props Getters ํŒจํ„ด
  } = useCheckout({ initialOrderIds, orders });

  const { PaymentInfoModal, handlePaymentInfoModal } = usePaymentInfoModal();

  return (
    <div className="flex flex-col gap-2">
      {renderList.map(([date, orders]) => (
        <CheckoutTableContainer
          key={date}
          {...getTableProps(date, orders)} // useCheckout ํ›…์ด ๋ฐ˜ํ™˜ํ•œ Props ์ „๋‹ฌ
        >
          {(tableBodyProps) => (
            <>
              <CheckoutTHeader />
              <CheckoutTBody
                {...tableBodyProps}
                handlePaymentInfoModal={handlePaymentInfoModal}
              />
            </>
          )}
        </CheckoutTableContainer>
      ))}
    </div>
  );
}
interface CheckoutTableContainerProps extends CheckoutTableData {
  tableClasses?: string;
  children: <T extends CheckoutTBodyData>(tableBodyProps: T) => JSX.Element;
}

export default function CheckoutTableContainer({
  // ...getCheckoutTableContainerProps ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœ์— ์ „๋‹ฌ๋ฐ›์€ Props ์ƒ๋žต
  children,
  ...tableBodyProps // ๋‚˜๋จธ์ง€ Props๋Š” ํ…Œ์ด๋ธ” Body์— ์‚ฌ์šฉ
}: CheckoutTableContainerProps) {
  return (
    <section className="bg-white py-4 max-w-[767px]">
      <div className="flex gap-3 py-4 px-4">{/* ... */}</div>
      <table className={tableClasses}>{children(tableBodyProps)}</table>
    </section>
  );
}

 

๋ฐฉ๋ฒ• 2 — Render Prop


render Prop ํ˜•ํƒœ๋กœ ์‚ฌ์šฉ

 

๊ผญ render Prop ์ด๋ฆ„์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค. renderFirstComponent ๋“ฑ์˜ ์ด๋ฆ„๋„ ๊ฐ€๋Šฅ.

export default function CheckoutMobile({ orders, initialOrderIds }: Props) {
  const {
    // ...
    getTableProps, // Native Props ๋…ธ์ถœ๋Œ€์‹  Props Getters ๋ชฉ๋ก์„ ์ œ๊ณตํ•˜๋Š” Props Getters ํŒจํ„ด
  } = useCheckout({ initialOrderIds, orders });

  const { PaymentInfoModal, handlePaymentInfoModal } = usePaymentInfoModal();

  return (
    <div className="flex flex-col gap-2">
      {renderList.map(([date, orders]) => (
        <CheckoutTableContainer
          {...getTableProps(date, orders)} // useCheckout ํ›…์ด ๋ฐ˜ํ™˜ํ•œ Props ์ „๋‹ฌ
          key={date}
          render={(tableBodyProps) => (
            <>
              <CheckoutTHeader />
              <CheckoutTBody
                {...tableBodyProps}
                handlePaymentInfoModal={handlePaymentInfoModal}
              />
            </>
          )}
        />
      ))}
    </div>
  );
}
interface CheckoutTableContainerProps extends CheckoutTableData {
  tableClasses?: string;
  render: <T extends CheckoutTBodyData>(tableBodyProps: T) => JSX.Element;
}

export default function CheckoutTableContainer({
  // ...getCheckoutTableContainerProps ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœ์— ์ „๋‹ฌ๋ฐ›์€ Props ์ƒ๋žต
  render,
  ...tableBodyProps // ๋‚˜๋จธ์ง€ Props๋Š” ํ…Œ์ด๋ธ” Body์— ์‚ฌ์šฉ
}: CheckoutTableContainerProps) {
  return (
    <section className="bg-white py-4 max-w-[767px]">
      <div className="flex gap-3 py-4 px-4">{/* ... */}</div>
      <table className={tableClasses}>{render(tableBodyProps)}</table>
    </section>
  );
}

 

๋ ˆํผ๋Ÿฐ์Šค


 


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