React

props, PropTypes, memo

sangchu 2023. 2. 18. 23:50

Props

아래 코드는 그닥 효율적인 코드가 아니다. 버튼 개수를 늘릴수록 복붙해야하는 양이 많아지기 때문이다.

const SaveBtn = () => {
      return (
        <button
          style={{
            backgroundColor: "tomato",
            color: "white",
            padding: "10px 20px",
            border: 0,
            borderRadius: 10,
          }}
        >
          Save Changes
        </button>
      );
    };
    const ConfirmBtn = () => {
      return (
        <button
          style={{
            backgroundColor: "tomato",
            color: "white",
            padding: "10px 20px",
            border: 0,
            borderRadius: 10,
          }}
        >
          Confirm
        </button>
      );
    };
    const App = () => {
      return (
        <div>
          <SaveBtn />
          <ConfirmBtn />
        </div>
      );
    };

결과 화면

컴포넌트를 새로운 정보를 props 인자로 전송하면서 재사용하자.

props는 부모 컴포넌트로부터 자식 컴포넌트에 데이터를 보낼 수 있게 해주는 방법이다. 부모에 props를 사용하면 자식 컴포넌트(함수)의 인자로 객체가 들어가게 된다. (컴포넌트는 단지 어떤 JSX를 반환하는 함수다.)

props는 자식으로 전달되는 유일한 인자이며, 부모에서 전송한 모든 props들을 갖는 object다.

const Btn = (props) => {
      return (
        <button
          style={{
            backgroundColor: "tomato",
            color: "white",
            padding: "10px 20px",
            border: 0,
            borderRadius: 10,
          }}
        >
          {props.text}
        </button>
      );
    };
    const App = () => {
      return (
        <div>
          <Btn text="Save Changes" />
          <Btn text="Continue" />
        </div>
      );
    };

위와 같이 작성하면 이전과 동일한 화면이 그려진다.

props는 객체이므로 아래와 같이 구조 분해 할당을 이용해서 사용할 수 있다.

const Btn = ({ text }) => {
      return (
        <button
          style={{
            backgroundColor: "tomato",
            color: "white",
            padding: "10px 20px",
            border: 0,
            borderRadius: 10,
          }}
        >
          {text}
        </button>
      );
    };
const App = () => {
      return (
        <div>
          <Btn text="Save Changes" />
          <Btn text="Continue" />
        </div>
      );
    };

 

props로 함수를 전달할 수도 있다.

<Btn text={value} onClick={changeValue} />

 

위 코드에서 onClick은 event listener가 아니라 props의 이름이다. 이름만 같을 뿐이다.

컴포넌트에는 HTML Element처럼 속성을 지정해줄 수 없다. props는 단지 컴포넌트 자식에 전송하는 데이터일 뿐이다.

const Btn = ({ text, onClick }) => {
			console.log(text, "was rendered");
      return (
        <button
          onClick={onClick}
          style={{
            backgroundColor: "tomato",
            color: "white",
            padding: "10px 20px",
            border: 0,
            borderRadius: 10,
          }}
        >
          {text}
        </button>
      );
    };
    const App = () => {
      const [value, setValue] = React.useState("Save Changes");
      const changeValue = () => setValue("Revert Changes");
      return (
        <div>
          <Btn text={value} onClick={changeValue} />
          <Btn text="Continue" />
        </div>
      );
    };

 

Memo

Save Changes 버튼을 눌렀는데, ReactJS 규칙에 의해 해당 컴포넌트가 리렌더링되면서 Continue 버튼까지 리렌더링 되는 것을 볼 수 있다(콘솔 확인)

Contunue 버튼의 props는 변한게 없으니 이는 원치 않은 결과다. 그럴 필요가 없기 때문이다.

memo를 통해 이를 해결할 수 있다. 괄호 안에는 적용할 자식 컴포넌트를 넣는다. 부모 컴포넌트는 memo로 설정한 이름으로 바꾼다.

const Btn = ({ text, onClick }) => {
      console.log(text, "was rendered");
      return (
        <button
          onClick={onClick}
          style={{
            backgroundColor: "tomato",
            color: "white",
            padding: "10px 20px",
            border: 0,
            borderRadius: 10,
          }}
        >
          {text}
        </button>
      );
    };
    const MemorizedBtn = React.memo(Btn);
    const App = () => {
      const [value, setValue] = React.useState("Save Changes");
      const changeValue = () => setValue("Revert Changes");
      return (
        <div>
          <MemorizedBtn text={value} onClick={changeValue} />
          <MemorizedBtn text="Continue" />
        </div>
      );
    };

ChangeValue 버튼을 누르면 props가 변경된 버튼만 리렌더링되는 것을 볼 수 있다.(콘솔 확인)

props가 변경됐다는 것은 state가 변경됐다는 것이다. 만약 부모가 어떤 state라도 변경이 있으면 모든 자식들은 다시 그려질 것이다(리렌더링). 이게 추후 어플리케이션이 느려지는 원인이 될 수도 있다(한 컴포넌트가 천 개의 컴포넌트를 갖고있을 때를 상상해보자..)

정리하자면, memo를 통해 props가 변경되지 않는다면 해당 컴포넌트는 리렌더링되지 않도록 설정할 수 있다.

컴포넌트가 React.memo()로 wrapping 될 때, React는 컴포넌트를 렌더링하고 결과를 메모이징(Memoizing)한다. 그리고 다음 렌더링이 일어날 때 props가 같다면, React는 메모이징된 내용을 재사용한다.

 

Prop Types

props에 전송하지 말아야 할 것을 전송하거나, 다른 타입을 보내는 경우 등의 실수를 방지하기 위해 사용한다.

reactJS는 어떤 타입의 prop를 받고 있는지 몰라서 의도와 다르게 코드를 작성해도 에러를 발생시키지 않는다.

propTypes를 설치함으로써 props의 타입이 뭐고 어떤 모양이어야 하는지 우리가 설명해줄 수 있다.

이를 설치하기 위해서 다음 코드를 넣어준다.

<script src="https://unpkg.com/prop-types@15.7.2/prop-types.js"></script>

다음과 같이 작성해보면, text는 string이어야 하는데 number를 넘겨서, fontSize는 number이어야 하는데 string를 넘겨서 에러가 뜬다.

const Btn = ({ text, fontSize }) => {
      return (
        <button
          style={{
            backgroundColor: "tomato",
            color: "white",
            padding: "10px 20px",
            border: 0,
            borderRadius: 10,
            fontSize, // 이름이 같으므로 이렇게 작성할 수 있음
          }}
        >
          {text}
        </button>
      );
    };
    Btn.propTypes = {
      text: PropTypes.string,
      fontSize: PropTypes.number,
    };
    const App = () => {
      return (
        <div>
          <Btn text={12} fontSize={18} />
          <Btn text="Save Changes" fontSize={"gk"} />
        </div>
      );
    };

이때 컴포넌트에 검사하고자 하는 props가 없으면 아무런 반응이 없다.

정확히 해당 props를 갖고 render 될 것이라는 것을 확실히 하고 싶으면 isRequired를 붙이면 된다.

Btn.propTypes = {
      text: PropTypes.string.isRequired,
      fontSize: PropTypes.number.isRequired,
    };

props를 전달할 때 다음과 같이 정의되지 않은 props에 default 값을 지정할 수 있다. 이는 js문법이다.

const Btn = ({ text, fontSize = 16 }) => {
      return (
        <button
          style={{
            backgroundColor: "tomato",
            color: "white",
            padding: "10px 20px",
            border: 0,
            borderRadius: 10,
            fontSize,
          }}
        >
          {text}
        </button>
      );
    };
    Btn.propTypes = {
      text: PropTypes.string.isRequired,
      fontSize: PropTypes.number,
    };
    const App = () => {
      return (
        <div>
          <Btn text="Save Changes" fontSize={18} />
          <Btn text="Continue" />
        </div>
      );
    };

 

 

 

참고 : NomadCoders - ReactJS로 영화 웹 서비스 만들기 강좌

'React' 카테고리의 다른 글

useEffect  (0) 2023.02.19
useState를 이용해 단위변환 기능 만들기  (0) 2023.02.18
state, setState  (0) 2023.02.18
[JSCODE] React 3회차 미션  (1) 2023.02.17
[JSCODE] React 2회차 심화미션  (1) 2023.02.14