최근 개발을 진행하면서 자식컴포넌트에 정의되어 있는 함수를 부모컴포넌트에서 사용해야 할 일이 생겼다.
부모 => 자식으로 넘어가는 일은 많았으나 자식 => 부모로 넘어가는 일은 자주있는 일이 아니다.
때문에 이렇게 정리를 해두려한다.
우선 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()
애초에 자식컴포넌트에 선언된 함수를 부모컴포넌트로 끌어사용하는 일을 만들지 말자...