본문 바로가기

카테고리 없음

React)자식 컴포넌트의 함수를 부모 컴포넌트에서 사용하기 with React.useImperativeHandle, React.forwardRef

최근 개발을 진행하면서 자식컴포넌트에 정의되어 있는 함수를 부모컴포넌트에서 사용해야 할 일이 생겼다.

부모  => 자식으로 넘어가는 일은 많았으나 자식  => 부모로 넘어가는 일은 자주있는 일이 아니다.

때문에 이렇게 정리를 해두려한다.

 

우선 useRef를 prop로 넘기는 법부터 알아보자

(내가 해결한 방식은 useRef를 자식컴포넌트에 넘기고, 그 자식컴포넌트의 함수를 부모컴포넌트에서 사용할 때였다. useRef와 함수는 전혀 상관이 없다.)

자식컴포넌트와 부모컴포넌트를 만들어준다.

Child.tsx

import React, { forwardRef } from "react";

const Child = () => {
  return (
    <div>
      <div id="child">Im Child Component!!</div>
      <h1>Props.number = </h1>
    </div>
  );
};

export default Child;

 

Parent.tsx

import React from "react";
import Child from "./Child";

const Parent = () => {
  const RefFromParent = useRef<null | HTMLDivElement>(null);
  const [number, setNumber] = useState<number>(9999);
  return (
    <div>
      <div>Im Parent Component!!</div>
      <Child />
    </div>
  );
};

export default Parent;

 

우선 부모컴포넌트에 선언된 RefFromParent를 자식 컴포넌트에 넘겨보자

방법은 굉장히 쉽다.

자식 컴포넌트를 forwardRef감싸주기만하면 된다.

Parent.tsx

import React, { useEffect, useRef, useState } from "react";
import Child from "./Child";

const Parent = () => {
  const RefFromParent = useRef<null | HTMLDivElement>(null);
  const [number, setNumber] = useState<number>(9999);

  useEffect(() => {
    if (!RefFromParent.current) return;
    console.log(RefFromParent.current);
  }, [number]);
  return (
    <div>
      <div>Im Parent Component!!</div>
      <Child RefFromParent={RefFromParent} number={number} />
      <button onClick={() => setNumber(number + 1)}>
        useRef 잘 넘겨졌나 확인하기
      </button>
    </div>
  );
};

export default Parent;

Child.tsx

import React, { forwardRef } from "react";

interface ChildPropTypes {
  RefFromParent: React.RefObject<HTMLDivElement>;
  number: number;
}

const Child = forwardRef(({ RefFromParent, number }: ChildPropTypes, ref) => {
  return (
    <div>
      <div id="child" ref={RefFromParent}>
        Im Child Component!!
      </div>
      <h1>Props.number = {number}</h1>
    </div>
  );
});

export default Child;

자 이제 부모 컴포넌트에 있는 버튼을 눌러보자.

버튼을 누르면 setState가 +1 증가하면서, RefFromParent를 콘솔창으로 찍을 것이다.

useRef가 자식컴포넌트의 Div에 잘 정착된 것을 확인할 수 있다.

그렇다면 자식컴포넌트에 있는 함수를 어떻게 부모컴포넌트에서 사용할 수 있을까?

우선 자식컴포넌트에 함수 하나를 작성해보자.

import React, { forwardRef } from "react";

interface ChildPropTypes {
  RefFromParent: React.RefObject<HTMLDivElement>;
  number: number;
}

const Child = forwardRef(({ RefFromParent, number }: ChildPropTypes, ref) => {
  const FunctionInChildComponent = () => {
    console.log("i'm in Child Component!!!");
  };
  return (
    <div>
      <div id="child" ref={RefFromParent}>
        Im Child Component!!
      </div>
      <h1>Props.number = {number}</h1>
    </div>
  );
});

export default Child;

콘솔을 찍는 간단한 FunctionInChildComponent를 하나 작성했다.

이제 이를 부모컴포넌트로 넘겨줄 것이다.

바로 useImpreativeHandel을 사용해서 말이다.

import React, { forwardRef } from "react";

interface ChildPropTypes {
  RefFromParent: React.RefObject<HTMLDivElement>;
  number: number;
}

const Child = forwardRef(({ RefFromParent, number }: ChildPropTypes, ref) => {
  React.useImperativeHandle(ref, () => ({ //useImperativeHandle added!!!!!!!!!!!
    FunctionInChildComponent,
  }));
  const FunctionInChildComponent = () => {
    console.log("i'm in Child Component!!!");
  };
  return (
    <div>
      <div id="child" ref={RefFromParent}>
        Im Child Component!!
      </div>
      <h1>Props.number = {number}</h1>
    </div>
  );
});

export default Child;

그리고 부모컴포넌트에서 ref를 하나 생성해 자식컴포넌트로 넘겨준다.

import React, { useEffect, useRef, useState } from "react";
import Child from "./Child";

const Parent = () => {
  const RefFromParent = useRef<null | HTMLDivElement>(null);
  const ParentRef = useRef<any>(); //added!!!!!!!
  const [number, setNumber] = useState<number>(9999);

  useEffect(() => {
    if (!RefFromParent.current) return;
    if (!ParentRef.current) return; //added!!!!!!!
    console.log(ParentRef.current); //added!!!!!!!
  }, [number]);
  return (
    <div>
      <div>Im Parent Component!!</div>
      <Child RefFromParent={RefFromParent}
      number={number} 
      ref={ParentRef} //added!!!!!!!
      />
      <button onClick={() => setNumber(number + 1)}>
        useRef 잘 넘겨졌나 확인하기
      </button>
    </div>
  );
};

export default Parent;

 

Parent.tsx에서 Parent.current를 찍어보면 자식컴포넌트에 선언된 함수가 들어있는 것을 볼 수가 있다.

만일 함수를 실행 싶다면,

ParentRef.current.FunctionInChildComponent()

 


애초에 자식컴포넌트에 선언된 함수를 부모컴포넌트로 끌어사용하는 일을 만들지 말자...