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

waterglassesยท2022๋…„ 12์›” 20์ผ
1

TIL

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

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

Ref์™€ DOM

Ref๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•  ๋•Œ

  • ํฌ์ปค์Šค, ํ…์ŠคํŠธ ์„ ํƒ์˜์—ญ, ํ˜น์€ ๋ฏธ๋””์–ด์˜ ์žฌ์ƒ์„ ๊ด€๋ฆฌ
  • ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ง์ ‘์ ์œผ๋กœ ์‹คํ–‰ํ•  ๋•Œ
  • ์„œ๋“œ ํŒŒํ‹ฐ DOM ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ React์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•  ๋•Œ

์„ ์–ธ์ ์œผ๋กœ ํ•ด๊ฒฐ๋  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ์—์„œ๋Š” ref์‚ฌ์šฉ์„ ์ง€์–‘

  • ์ฆ‰, state๋‚˜ prop์œผ๋กœ ํ•ด๊ฒฐ๋  ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ์„ ์–˜๊ธฐํ•œ๋‹ค.
  • Dialogย ์ปดํฌ๋„ŒํŠธ์—์„œ ์ฐฝ์„ ์—ด๊ณ  ๋‹ซ์„ ๋•Œย ref๋กœ DOM ๋…ธ๋“œ์— ์ ‘๊ทผํ•˜์—ฌย open(), close()
    ๋ฉ”์„œ๋“œ๋ฅผ ๋ฐ”์ธ๋”ฉํ•˜์ง€๋ง๊ณ ย isOpen๊ณผ ๊ฐ™์€ย stateย ๋˜๋Š”ย prop์„ ๋„˜๊ฒจ์ฃผ์–ด ํ•ด๊ฒฐํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

Ref์— ์ ‘๊ทผํ•˜๊ธฐ

// render ๋ฉ”์„œ๋“œ ์•ˆ์—์„œ ref๊ฐ€ ์—˜๋ฆฌ๋จผํŠธ์—๊ฒŒ ์ „๋‹ฌ๋˜์—ˆ์„ ๋•Œ,
// ๊ทธ ๋…ธ๋“œ๋ฅผ ํ–ฅํ•œ ์ฐธ์กฐ๋Š” ref์˜ current ์–ดํŠธ๋ฆฌ๋ทฐํŠธ์— ๋‹ด๊ธฐ๊ฒŒ ๋œ๋‹ค.
const node = this.myRef.current;

ref ์–ดํŠธ๋ฆฌ๋ทฐํŠธ๊ฐ€ HTML ์—˜๋ฆฌ๋จผํŠธ์— ์“ฐ์˜€์„ ๋•Œ

์ƒ์„ฑ์ž์—์„œย React.createRef()๋กœ ์ƒ์„ฑ๋œย ref๋Š” ์ž์‹ ์„ ์ „๋‹ฌ๋ฐ›์€ DOM ์—˜๋ฆฌ๋จผํŠธ๋ฅผย currentย ํ”„๋กœํผํ‹ฐ์˜ ๊ฐ’์œผ๋กœ์„œ ๋ฐ›๋Š”๋‹ค.

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = React.createRef();
    this.focusTextInput = this.focusTextInput.bind(this);
  }

  focusTextInput() {
    this.textInput.current.focus();
  }

  render() {
    return (
      <div>
        <input
          type="text"
          ref={this.textInput} />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}

ref ์–ดํŠธ๋ฆฌ๋ทฐํŠธ๊ฐ€ ์ปค์Šคํ…€ ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์— ์“ฐ์˜€์„ ๋•Œ

refย ๊ฐ์ฒด๋Š” ๋งˆ์šดํŠธ๋œ ์ปดํฌ๋„ŒํŠธ์˜ ์ธ์Šคํ„ด์Šค๋ฅผย currentย ํ”„๋กœํผํ‹ฐ์˜ ๊ฐ’์œผ๋กœ์„œ ๋ฐ›์Šต๋‹ˆ๋‹ค.

class AutoFocusTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = React.createRef();
  }

  componentDidMount() {
    this.textInput.current.focusTextInput();
  }

  render() {
    return (
			// CustomTextInput์ด ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์ผ ๋•Œ๋งŒ ์ž‘๋™
			// class CustomTextInput extends React.Component{ }
      <CustomTextInput ref={this.textInput} />
    );
  }
}

ref ์–ดํŠธ๋ฆฌ๋ทฐํŠธ๋Š” ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

function MyFunctionComponent() { // ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ
  return <input />;
}

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = React.createRef();
  }
  render() {
    // ์ด ์ฝ”๋“œ๋Š” ๋™์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค.
    return (
      <MyFunctionComponent ref={this.textInput} />
    );
  }
}

๋‹จ, DOM ์—˜๋ฆฌ๋จผํŠธ๋‚˜ ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์˜ ์ธ์Šคํ„ด์Šค์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ดย refย ์–ดํŠธ๋ฆฌ๋ทฐํŠธ๋ฅผ ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๋œ๋‹ค.

function CustomTextInput(props) {
  // textInput์€ ref ์–ดํŠธ๋ฆฌ๋ทฐํŠธ๋ฅผ ํ†ตํ•ด ์ „๋‹ฌ๋˜๊ธฐ ์œ„ํ•ด์„œ ๊ผญ 
  const textInput = useRef(null);

  function handleClick() {
    textInput.current.focus();
  }

  return (
    <div>
      <input
        type="text"
        ref={textInput} />
      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );
}

๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—๊ฒŒ DOM ref๋ฅผ ๊ณต๊ฐœํ•˜๊ธฐ

ref ์ „๋‹ฌํ•˜๊ธฐ๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์˜ย ref๋ฅผ ์ž์‹ ์˜ย ref๋กœ๋ถ€ํ„ฐ ์™ธ๋ถ€์— ๋…ธ์ถœ์‹œํ‚ค๊ฒŒ ํ•œ๋‹ค.

๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ย ref๋ฅผ ๋‚ด๋ ค์ฃผ๋ฉด ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋Š” ์ „๋‹ฌ๋ฐ›์€ย ref์— ์ž์‹ ์˜ย ref๋ฅผ ๋‹ด์•„ ์˜ฌ๋ ค์ฃผ๋Š” ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.

์ฝœ๋ฐฑ ref

  • React 16.3 ์ด์ „๋ฒ„์ „์ด๋ผ๋ฉด ์ฝœ๋ฐฑ ref๋ฅผ ๋Œ€์‹  ์‚ฌ์šฉ

Render Props

ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ๋ฅผ ์œ„ํ•œ render props ์‚ฌ์šฉ๋ฒ•

// ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋งˆ์šฐ์Šค ์œ„์น˜๋ฅผ ์ถ”์ ํ•˜๋Š” ๋กœ์ง
class MouseTracker extends React.Component {
  constructor(props) {
    super(props);
    this.handleMouseMove = this.handleMouseMove.bind(this);
    this.state = { x: 0, y: 0 };
  }
	
  // ์Šคํฌ๋ฆฐ ์ฃผ์œ„๋กœ ๋งˆ์šฐ์Šค ์ปค์„œ๋ฅผ ์›€์ง์ด๋ฉด, ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šฐ์Šค์˜ (x, y) ์ขŒํ‘œ๋ฅผ <p>์— ๋‚˜ํƒ€๋‚จ
  handleMouseMove(event) {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  }

  render() {
    return (
      <div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
        <h1>Move the mouse around!</h1>
        <p>The current mouse position is ({this.state.x}, {this.state.y})</p>
      </div>
    );
  }
}

๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ฝ”๋“œ๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๋ ค๋ฉด?

class Cat extends React.Component {
  render() {
    const mouse = this.props.mouse;
    return (
      <img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
    );
  }
}

class Mouse extends React.Component {
  constructor(props) {
    super(props);
    this.handleMouseMove = this.handleMouseMove.bind(this);
    this.state = { x: 0, y: 0 };
  }

  handleMouseMove(event) {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  }

  render() {
    return (
      <div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>

        {/*
          <Mouse>๊ฐ€ ๋ฌด์—‡์„ ๋ Œ๋”๋งํ•˜๋Š”์ง€์— ๋Œ€ํ•ด ๋ช…ํ™•ํžˆ ์ฝ”๋“œ๋กœ ํ‘œ๊ธฐํ•˜๋Š” ๋Œ€์‹ ,
          `render` prop์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฌด์—‡์„ ๋ Œ๋”๋งํ• ์ง€ ๋™์ ์œผ๋กœ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
        */}
        {this.props.render(this.state)}
      </div>
    );
  }
}

class MouseTracker extends React.Component {
  render() {
    return (
      <div>
        <h1>Move the mouse around!</h1>
				{// render ํ•จ์ˆ˜์— prop์œผ๋กœ ์ „๋‹ฌํ•ด์คŒ } 
        <Mouse render={mouse => (
          <Cat mouse={mouse} />
        )}/>
      </div>
    );
  }
}

์ฆ‰, render prop์€ ๋ฌด์—‡์„ ๋ Œ๋”๋งํ• ์ง€ ์ปดํฌ๋„ŒํŠธ์— ์•Œ๋ ค์ฃผ๋Š” ํ•จ์ˆ˜์ด๋‹ค.

HOC(higher-order-components)ํŒจํ„ด์— render props pattern์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

function withMouse(Component) {
  return class extends React.Component {
    render() {
      return (
        <Mouse render={mouse => (
          <Component {...this.props} mouse={mouse} />
        )}/>
      );
    }
  }
}

render ์ด์™ธ์˜ Props ์‚ฌ์šฉ๋ฒ•

์–ด๋–ค ํ•จ์ˆ˜ํ˜• prop์ด๋“  render prop์ด ๋  ์ˆ˜ ์žˆ๋‹ค.

// render๋ณด๋‹ค children์„ ๋” ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
<Mouse children={mouse => (
  <p>The mouse position is {mouse.x}, {mouse.y}</p>
)}/>

์ฃผ์˜์‚ฌํ•ญ

React.PureComponent์—์„œ render props pattern์„ ์‚ฌ์šฉํ•  ๋• ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค.

class Mouse extends React.PureComponent {
  // ์œ„์™€ ๊ฐ™์€ ๊ตฌํ˜„์ฒด...
}

class MouseTracker extends React.Component {
  render() {
    return (
      <div>
        <h1>Move the mouse around!</h1>
        {/*
         <MouseTracker>๊ฐ€ ๋ Œ๋”๋ง ๋  ๋•Œ๋งˆ๋‹ค <Mouse render>์˜ prop์œผ๋กœ ๋„˜์–ด๊ฐ€๋Š”
					ํ•จ์ˆ˜๊ฐ€ ๊ณ„์† ์ƒˆ๋กœ ์ƒ์„ฑ๋œ๋‹ค.
        */}
        <Mouse render={mouse => (
          <Cat mouse={mouse} />
        )}/>
      </div>
    );
  }
}

๋”ฐ๋ผ์„œ ์•„๋ž˜์ฒ˜๋Ÿผ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ prop์„ ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค.

class MouseTracker extends React.Component {
  // `this.renderTheCat`๋ฅผ ํ•ญ์ƒ ์ƒ์„ฑํ•˜๋Š” ๋งค์„œ๋“œ๋ฅผ ์ •์˜
  // ์ด๊ฒƒ์€ render๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋งˆ๋‹ค *๊ฐ™์€* ํ•จ์ˆ˜๋ฅผ ์ฐธ์กฐ
  renderTheCat(mouse) {
    return <Cat mouse={mouse} />;
  }

  render() {
    return (
      <div>
        <h1>Move the mouse around!</h1>
        <Mouse render={this.renderTheCat} />
      </div>
    );
  }
}

Refer

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

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