๐Ÿ’ป Ref ์ „๋‹ฌํ•˜๊ธฐ(feat. ๋ฆฌ์•กํŠธ ๊ณต์‹๋ฌธ์„œ)

waterglassesยท2022๋…„ 11์›” 2์ผ
0

TIL

๋ชฉ๋ก ๋ณด๊ธฐ
44/50
post-thumbnail

โš ๏ธ ์ •๋ฆฌํ•œ ๋‚ด์šฉ์€ ์˜คํƒ€๋‚˜ ์ž˜๋ชป๋œ ์ •๋ณด๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋Œ“๊ธ€๋กœ ์•Œ๋ ค์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

ref ์ „๋‹ฌ์€ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ†ตํ•ด ์ž์‹ ์ค‘ ํ•˜๋‚˜์— ref๋ฅผ ์ž๋™์œผ๋กœ ์ „๋‹ฌํ•˜๋Š” ๊ธฐ๋ฒ•์ด๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋Œ€๋ถ€๋ถ„์˜ ์ปดํฌ๋„ŒํŠธ์— ํ•„์š”ํ•˜์ง€๋Š” ์•Š๋‹ค. ๊ทธ๋ ‡์ง€๋งŒ, ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ๊ฐ™์€ ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ์œ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

DOM์— refs ์ „๋‹ฌํ•˜๊ธฐ

function FancyButton(props) {
  return (
    <button className="FancyButton">
      {props.children}
    </button>
  );
}

FancyButton ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋“ค์€ย ์ผ๋ฐ˜์ ์œผ๋กœย ๋‚ด๋ถ€ย buttonย DOM ์š”์†Œ์— ๋Œ€ํ•œ ref๋ฅผ ์–ป์„ ํ•„์š”๊ฐ€ ์—†๋‹ค. ์ด๋Š” ์ปดํฌ๋„ŒํŠธ๋“ค์ด ์„œ๋กœ์˜ DOM ๊ตฌ์กฐ์— ์ง€๋‚˜์น˜๊ฒŒ ์˜์กดํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๊ดœ์ฐฎ๋‹ค.

์ด๋Ÿฐ ์บก์Šํ™”๋Š”ย FeedStory๋‚˜ย Commentย ๊ฐ™์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ๋ฒจ์˜ ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ๋ฐ”๋žŒ์งํ•˜์ง€๋งŒ,ย FancyButton์ด๋‚˜ย MyTextInput๊ณผ ๊ฐ™์€ ์žฌ์‚ฌ์šฉ์„ฑ์ด ๋†’์€ ๋ง๋‹จ ์š”์†Œ(Leaf์š”์†Œ)์—์„œ๋Š” ๋ถˆํŽธํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ์ด๋Ÿฐ ์ปดํฌ๋„ŒํŠธ๋“ค์€ ์ผ๋ฐ˜์ ์ธ DOM, button, input๊ณผ ์œ ์‚ฌํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด์— ๊ฑธ์ณ ์‚ฌ์šฉ๋˜๋Š” ๊ฒฝํ–ฅ์ด ์žˆ๋‹ค.

React ๊ธฐ์ˆ  ์šฉ์–ด ๋ชจ์Œ - React

์—ฌ๊ธฐ์„œ FeedStory๋‚˜ Comment ๋Š” ๋ณต์žกํ•œ UI(App, FeedStory, Comment)๋ฅผ ๋งํ•œ๋‹ค.

Ref ์ „๋‹ฌํ•˜๊ธฐ๋Š” ์ผ๋ถ€ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ˆ˜์‹ ํ•œ ref๋ฅผ ๋ฐ›์•„ ์กฐ๊ธˆ ๋” ์•„๋ž˜๋กœ ์ „๋‹ฌ ํ•  ์ˆ˜ ์žˆ๋Š” ์˜ตํŠธ์ธ ๊ธฐ๋Šฅ์ด๋‹ค.

const FancyButton = React.forwardRef((props, ref) => (
// 3. React๋Š” ์ด ref๋ฅผ forwardRef ๋‚ด๋ถ€์˜ (props, ref) => ... ํ•จ์ˆ˜์˜ ๋‘ ๋ฒˆ์งธ ์ธ์ž๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.
// 4. ์ด ref๋ฅผ JSX ์†์„ฑ์œผ๋กœ ์ง€์ •ํ•ด์„œ <button ref={ref}>์œผ๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.
// 5. ref๊ฐ€ ์ฒจ๋ถ€๋˜๋ฉด ref.current๋Š” <button> DOM ๋…ธ๋“œ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>));

// ์ด์ œ DOM ๋ฒ„ํŠผ์œผ๋กœ ref๋ฅผ ์ž‘์ ‘ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
const ref = React.createRef(); 
// 1. React.createRef๋ฅผ ํ˜ธ์ถœํ•ด์„œ React ref๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ref ๋ณ€์ˆ˜์— ํ• ๋‹นํ•œ๋‹ค.
<FancyButton ref={ref}>Click me!</FancyButton>;
// 2. ref๋ฅผ JSX ์†์„ฑ์œผ๋กœ ์ง€์ •ํ•ด์„œ <FancyButton ref={ref}>๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

์œ„์—์„œ FancyButton์€ React.forwardRef๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ „๋‹ฌ๋œ ref๋ฅผ ์–ป๊ณ  ๊ทธ๊ฒƒ์„ ๋ Œ๋”๋ง๋˜๋Š” DOM ๋ฒ„ํŠผ์œผ๋กœ ์ „๋‹ฌํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ FancyButton์„ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋“ค์€ button DOM ๋…ธ๋“œ์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

์ปดํฌ๋„ŒํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์œ ์ง€๊ด€๋ฆฌ์ž๋ฅผ ์œ„ํ•œ ์ฃผ์˜์‚ฌํ•ญ

์ปดํฌ๋„ŒํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œย forwardRef๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์‹œ์ž‘ํ•  ๋•Œ ์ด๊ฒƒ์„ ๋ณ€๊ฒฝ์‚ฌํ•ญ์œผ๋กœ ๊ฐ„์ฃผํ•˜๊ณ  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์ƒˆ๋กœ์šด ์ค‘์š” ๋ฒ„์ „์„ ๋ฆด๋ฆฌ์ฆˆ ํ•ด์•ผ ํ•œ๋‹ค. ์ด๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์ฃผ๋ชฉํ•  ๋งŒํ•˜๊ฒŒ ๋‹ค๋ฅธ ๋™์ž‘์„ ํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๊ณ  ์ด์ „ ๋™์ž‘์— ์˜์กดํ•˜๋Š” ์•ฑ์ด๋‚˜ ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์ด ์†์ƒ๋  ๊ฐ€๋Šฅ์„ฑ์ด ํฌ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๊ณ ์ฐจ ์ปดํฌ๋„ŒํŠธ(HOC, Higher Order Component)์—์„œ์˜ ref ์ „๋‹ฌํ•˜๊ธฐ

function logProps(WrappedComponent) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  }

  return LogProps;

logPropsHOC๋Š” ๋ชจ๋“ ย props๋ฅผ ๋ž˜ํ•‘ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌํ•˜๋ฏ€๋กœ ๋ Œ๋”๋ง ๋œ ๊ฒฐ๊ณผ๊ฐ€ ๋™์ผํ•˜๊ฒŒ ๋œ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์ด HOC๋ฅผ ์‚ฌ์šฉํ•ด์„œ FancyButton ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌํ•˜๋Š” ๋ชจ๋“  props๋ฅผ ๊ธฐ๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค.

class FancyButton extends React.Component {
  focus() {
    // ...
  }

  // ...
}

// FancyButton์„ ๋‚ด๋ณด๋‚ด๋Š” ๋Œ€์‹  LogProps๋ฅผ ๋‚ด๋ณด๋ƒ…๋‹ˆ๋‹ค.
// ๊ทธ๋ž˜๋„ FancyButton์„ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.
export default logProps(FancyButton);

ref๋Š” prop์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— refs๋Š” ์ „๋‹ฌ๋˜์ง€ ์•Š๋Š”๋‹ค.ย key์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœย ref๋Š” React์—์„œ ๋‹ค๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•œ๋‹ค. HOC์— ref๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ref๋Š” ๋ž˜ํ•‘ ๋œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ ๊ฐ€์žฅ ๋ฐ”๊นฅ์ชฝ ์ปจํ…Œ์ด๋„ˆ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฐธ์กฐํ•œ๋‹ค.

์ฆ‰, FancyButtonย ์ปดํฌ๋„ŒํŠธ๋ฅผ ์œ„ํ•œ refs๊ฐ€ ์‹ค์ œ๋กœ๋Š”ย LogPropsย ์ปดํฌ๋„ŒํŠธ์— ์ฒจ๋ถ€๋œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.

import FancyButton from './FancyButton';

const ref = React.createRef();

// ๊ฐ€์ ธ์˜จ FancyButton ์ปดํฌ๋„ŒํŠธ๋Š” LogProps HOC์ด๋‹ค..
// ๋ Œ๋”๋ง๋œ ๊ฒฐ๊ณผ๊ฐ€ ๋™์ผํ•˜๋‹ค๊ณ  ํ•˜๋”๋ผ๋„, ref๋Š” ๋‚ด๋ถ€ FancyButton ์ปดํฌ๋„ŒํŠธ ๋Œ€์‹  LogProps๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค.
// ์ด๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ ์˜ˆ๋ฅผ ๋“ค์–ด ref.current.focus()๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์„ ํ•œ๋‹ค..

<FancyButton
  label="Click Me"
  handleClick={handleClick}
  ref={ref}
/>;

React.forwardRefย API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‚ด๋ถ€ย FancyButtonย ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•œ refs๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

function logProps(Component) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }

    render() {
      const {forwardedRef, ...rest} = this.props;

      // ์‚ฌ์šฉ์ž ์ •์˜ prop "forwardedRef"๋ฅผ ref๋กœ ํ• ๋‹นํ•œ๋‹ค.
      return <Component ref={forwardedRef} {...rest} />;
    }
  }

  // React.forwardRef์—์„œ ์ œ๊ณตํ•˜๋Š” ๋‘ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ "ref"์— ์ฃผ์˜ํ•ด์•ผํ•œ๋‹ค.
  // ๊ฐ€๋ น "forwardedRef"๊ฐ™์€ ์ผ๋ฐ˜ prop์œผ๋กœ LogProps์— ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  // ๊ทธ ๋‹ค์Œ Component์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.
  return React.forwardRef((props, ref) => {
    return <LogProps {...props} forwardedRef={ref} />;
  });
}

DevTools์— ์‚ฌ์šฉ์ž ์ •์˜ ์ด๋ฆ„ ํ‘œ์‹œํ•˜๊ธฐ

React DevTools๋Š” ์ด ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ref ์ „๋‹ฌ ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•ด์„œ ๋ฌด์—‡์„ ํ‘œ์‹œํ•  ๊ฒƒ์ธ์ง€ ์ •์˜ํ•œ๋‹ค. ๋ Œ๋”๋ง ํ•จ์ˆ˜๋ฅผ ์ง€์ •ํ•˜๋ฉด DevTools์— ํ•ด๋‹น ์ด๋ฆ„๋„ ํฌํ•จ๋œ๋‹ค.

const WrappedComponent = React.forwardRef(
  function myFunction(props, ref) {
    return <LogProps {...props} forwardedRef={ref} />;
  }
);

๐Ÿ”ฅ ๋Š๋‚€์ 

React.forwardRef๋ฅผ ์‚ฌ์šฉํ•ด์„œ ref๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๊ณ  HOC์™€ ์ด ๋ถ€๋ถ„์„ ์ž˜ ํ™œ์šฉํ•˜๋ฉด ์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„๋กœ UI๋ฅผ ๊ตฌ์„ฑํ•  ๋•Œ ๋” ํŽธ๋ฆฌํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค.

Refer

profile
๋งค ์ˆœ๊ฐ„ ์„ฑ์žฅํ•˜๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜๋ ค๊ณ  ๋…ธ๋ ฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€