그림 그리는 개발자
  • 자식 컴포넌트에 전달하는 props 관련 테스트
    2022년 05월 29일 09시 58분 37초에 업로드 된 글입니다.
    작성자: 루루개발자

     

    안녕하세요. 루루개발자 입니다.

    이번 시간에는 리액트에서 자식 컴포넌트에 전달하는 props 값들의 변화 감지에 대해 테스트를 진행해보려고 합니다.

     

    먼저 부모컴포넌트는 아래와 같이 작성하였습니다.

     

    -- index.tsx

    import { useCallback, useRef, useState } from "react";
    import { TestChildComponent } from "../../../src/components-in/test/test-child-component/test-child-component";
    
    const Index = () => {
      const [name1, setName1] = useState('홍길동');
      const name2 = useRef('아이유');
    
      const renderLog = useCallback(() => {
        console.log('부모 컴포넌트 렌더링 됨!');
        return '';
      }, []);
    
      const changeName1ButtonClick = useCallback((e: any) => {
        setName1('흰둥이');
      }, []);
    
      const changeName2ButtonClick = useCallback((e: any) => {
        name2.current = '에일리';
      }, []);
    
      return (
        <div>
          <div>
            { renderLog() }
            <TestChildComponent name={name1}></TestChildComponent>
          </div>
          <div>
            <button onClick={changeName1ButtonClick}>name1 변경</button>
            <button onClick={changeName2ButtonClick}>name2 변경</button>
          </div>
        </div>
      );
    };
    
    export default Index;

     

    그리고 테스트용 자식 컴포넌트는 다음과 같이 작성하였습니다.

    import { useCallback, useEffect } from "react";
    
    interface Props {
      name: string;
    }
    
    export const TestChildComponent = (props: Props) => {
      useEffect(() => {
        console.log('TestChildComponent의 useEffect 호출됨!');
      }, []);
    
      useEffect(() => {
        console.log('TestChildComponent의 useEffect 2 에서 props.name 변화 감지!');
      }, [props.name]);
    
      const renderLog = useCallback(() => {
        console.log('자식 컴포넌트 렌더링 됨!');
        return '';
      }, []);
    
      return (
        <div>
          { renderLog() }
          <div>
            [TestChildComponent]
          </div>
          <div>
            이름은 <span>{ props.name }</span> 입니다.
          </div>
        </div>
      );
    };

     

     

    위와 같이 작성 하고 해당 부모컴포넌트 페이지에 접근해보면 화면과 콘솔창엔 다음과 같이 표시됩니다.

     

    자식컴포넌트에 부모컴포넌트의 name1 값을 props.name 으로 전달하여 자식컴포넌트는 그 props.name 값을 화면에 보여주고 있는 상태입니다. 이 상태에서 부모컴포넌트의 "name1 변경" 버튼을 클릭해 name1 의 값을 변경하겠습니다.

     

     

    name1 은 useState 로 설정된 변수로, 값이 변경되어 부모 컴포넌트가 재렌더링 되고 자식 컴포넌트 또한 재렌더링 된 것을 확인 할 수 있습니다. 더불어 자식 컴포넌트에서는 props.name 의 변화를 감지하는 useEffect 도 정상적으로 호출 된 것을 확인 할 수 있습니다.

     

     

     

    하지만 이번에는 useRef 로 지정된 name2 값으로 변경해보겠습니다.

    부모컴포넌트의 코드를 다음과 같이 수정합니다.

    import { useCallback, useRef, useState } from "react";
    import { TestChildComponent } from "../../../src/components-in/test/test-child-component/test-child-component";
    
    const Index = () => {
      const [name1, setName1] = useState('홍길동');
      const name2 = useRef('아이유');
    
      const renderLog = useCallback(() => {
        console.log('부모 컴포넌트 렌더링 됨!');
        return '';
      }, []);
    
      const changeName1ButtonClick = useCallback((e: any) => {
        setName1('흰둥이');
      }, []);
    
      const changeName2ButtonClick = useCallback((e: any) => {
        name2.current = '에일리';
      }, []);
    
      return (
        <div>
          <div>
            { renderLog() }
            <TestChildComponent name={name2.current}></TestChildComponent>
          </div>
          <div>
            <button onClick={changeName1ButtonClick}>name1 변경</button>
            <button onClick={changeName2ButtonClick}>name2 변경</button>
          </div>
        </div>
      );
    };
    
    export default Index;

     

     

    TestChildComponent 의 props.name 에 넘겨주는 값을 부모컴포넌트의 name2.current 로 변경하면 브라우저 화면과 콘솔창에는 다음과 같이 표시됩니다.

     

    이 상태에서 "name2 변경" 버튼을 클릭하면 화면에 변화도 없고 콘솔 창에도 아무런 변화가 없습니다.

    이어서 바로 "name1 변경" 버튼을 클릭하면 다음과 같은 결과가 나타납니다.

     

    이걸로 알 수 있는 결론은, 부모컴포넌트에서 자식컴포넌트의 props 로 넘겨준 값은 부모컴포넌트가 재렌더링 되어야 자식컴포넌트에 전달된다는 사실입니다. 하지만 꼭 부모컴포넌트가 재렌더링되지 않더라도 자식컴포넌트에서 부모컴포넌트에서 바뀐 값을 참조할 수 있는 방법이 있는데 바로 부모컴폰너트에서 자식컴포넌트에 useRef 로 생성한 값을 넘길 때 .current 가 아닌 ref 객체 자체를 넘기는 방법입니다. 이렇게 되면 부모컴포넌트에서 ref.current = '..' 등과 같이 값이 변경되면 자식컴포넌트에서도  ref.current 으로 바뀐 값을 바로 참조할 수 있습니다. 물론 재렌더링이 이루어지지 않았기 때문에 화면에서는 이전 값이 계속 표시됩니다. useRef 는 useState와 달리 값이 변경되도 화면을 다시 그리지 않으니까요. 그리고 useEffect 로는 useRef 의 값 변경을 감지할 수 없다는 것도 이번에 테스트를 하면서 알게 되었습니다.

     

    또, 부가적으로 테스트해본 결과 props 밑에 있는 string, number 등의 값들은 자식컴포넌트에서 props.name = '..', props.age = .., 등과 같이 수정이 불가능했습니다. 하지만 props 밑에 객체가 있다면 해당 객체 밑에 있는 값들은 props.obj.name = '...', props.obj.age = .. 등과 같이 수정이 가능했습니다.

     

     

    테스트 결과를 정리해보면 다음과 같습니다.

     

     

    1. 부모컴포넌트에서 자식컴포넌트에 props (객체가 아닌 string 또는 number 등의) 값을 넘기고 부모컴포넌트에서 해당 값이 변경되면 재렌더링이 일어나야 자식컴포넌트에 전달된다.

     

    2. 부모컴포넌트에서 자식컴포넌트의 props 에 useRef 로 생성한 객체를 넘기면 값이 변경될 경우, 리렌더링이 일어나지 않아도 부모 및 자식 컴포넌트에서 바로 변경된 값 참조가 가능하다. (단, 화면에는 리렌더링이 일어나기 전까지는 이전 값이 계속 표시된다.)

     

    3. useEffect 는 useRef 의 값 변경을 감지할 수 없다.

     

    4. props 밑의 값들은 기본적으로 readonly 이지만, props 밑의 객체 같은 경우 해당 객체 내의 값을 변경 할 수는 있다.

     

     

    이상입니다.

    읽어주셔서 감사합니다. :)

     

    댓글