React | props, memo, propTypes, defaultProps

Sujeong Ji·2022년 4월 22일
0

React

목록 보기
7/9
post-thumbnail

keyword props, memo, React.memo(), propTypes, isRequired, PropTypes.oneOfType([,]), defaultProps


Props

부모컴포넌트에서 자식컴포넌트로 데이터를 보낼 수 있게 해주는 일종의 방식이자 argument의 역할

이전 예제인 converters에서는 자식컴포넌트(MinutesToHours, KmToMiles)는 부모컴포넌트의 데이터를 필요로 하지 않고 독립적으로 존재 가능했음.

🌼 이번 포스트에서는,

  • 부모컴포넌트 -> 자식컴포넌트로 데이터를 보내기
  • 리액트는 부모컴포넌트에서 커스터마이즈된 props(properties)들을 {Object}에 넣어서 자식컴포넌트에 첫번째 인자로 보냄
    • String (text)
    • true/false
    • style (backgroudnColor, fontSize 등)
    • 함수
  • props객체임.
  • props 객체의 속성은 (props) - {props.속성} 또는 ({속성}) - {속성}으로 사용 가능
  • React.memo()로 모든 자식컴포넌트의 불필요한 자동 리렌더링 방지 및 성능 최적화 가능
  • PropTypes으로 props 데이터타입값 설정 및 에러 통보 가능
    • isRequired 추가하면 props 필수값으로 설정 가능
    • PropTypes.oneOfType([,])로 설정하면, 여러 데이터값 중 하나면 만족으로 설정 가능
  • defaultProps으로 props 초기값 설정 가능

예제 configuring text를 해보자!

  • 예제: 공통으로 HTML+CSS Styling된 자식컴포넌트안에 세부내역 text만 바꿔보기

1. 기본 버튼 2개 만들기

<script type="text/babel">
          
  function SaveBtn() {
  return (

    <button
    style={{
    backgroundColor: "tomato",
    color: "white",
    padding: "10px 20px",
    border:0,
    borderRadius:10
    }}>Save Changes</button>
)
}

function ConfirmBtn() {
  return (

    <button
    style={{
    backgroundColor: "tomato",
    color: "white",
    padding: "10px 20px",
    border:0,
    borderRadius:10
    }}>Confirm</button>
  )
}

function App() {


  return (
    <div>
    <SaveBtn />
    <ConfirmBtn />
    </div>
  )
};

const root = document.getElementById("root");
ReactDOM.render(<App />, root);
</script>

부모컴포넌트 -> 자식컴포넌트로
props 통해 String (text) 보내기

2. props 보내기 - 'text' 개별화 커스터마이징
console.log(props) 찍어보면, props가 객체로 넘겨지는데, 이때 커스터마이징한 속성들은 객체의 첫 인자로 들어감.

개별 text props 만들어 주기

<Btn text="Save Change" important={true}/>
<Btn text="Confirm" important={false}/>

props 객체로 전달해서 UI 출력

function Btn(props) {
  console.log(props) // Object
  return (

    <button
      style={{
      backgroundColor: "tomato",
      color: "white",
      padding: "10px 20px",
      border:0,
      borderRadius:10
    }}>{props.text}</button>
)
}

전체코드

function Btn( props ) {
  console.log(props) // Object
  return (

    <button
    style={{
    //backgroundColor: "/tomato",
    backgroundColor: props.important? "tomato":"lightblue",
    color: "white",
    padding: "10px 20px",
    border:0,
    borderRadius:10
    }}>{props.text}</button>
)
}

function App() {


  return (
    <div>
    <Btn text="Save Change" important={true}/>
	<Btn text="Confirm" important={false}/>
  </div>
)
};

부모컴포넌트 -> 자식컴포넌트로
props 통해 true/false 보내기

3. props 보내기 - 다른 방식으로 보내기, T/F도 추가

📌 props는 Object라 {}로 받을 수 있음.

function Btn({ text, important })
<button
      style={{
        //backgroundColor: "/tomato",
        backgroundColor: important? "tomato":"lightblue",
        color: "white",
        padding: "10px 20px",
        border:0,
        borderRadius:10
    }}>{text}
</button>

📌 String뿐만 아니라, true/false도 props로 보내기 가능

<Btn text="Save Change" important={true}/>
<Btn text="Confirm" important={false}/>

전체 코드

function Btn({ text, important }) {
  //console.log(props) // Object
  return (

    <button
      style={{
      //backgroundColor: "/tomato",
      backgroundColor: important? "tomato":"lightblue",
      color: "white",
      padding: "10px 20px",
      border:0,
      borderRadius:10
    }}>{text}</button>
)
}

function App() {


  return (
    <div>
    <Btn text="Save Change" important={true}/>
	<Btn text="Confirm" important={false}/>
  </div>
)
};

최종 코드

<!DOCTYPE html>
<html>
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone@7.17.6/babel.min.js"></script>
    <script type="text/babel">
          
        function Btn({ text, important, backgroundColor }) {
            //console.log(props) // Object
            return (

                <button
                    style={{
                        //backgroundColor: "/tomato",
                        backgroundColor: important? "tomato": "lightblue",
                        color: "white",
                        padding: "10px 20px",
                        border:0,
                        borderRadius:10
                    }}>{text}</button>
            )
        }
      
        function App() {
        
          
        return (
            <div>
                <Btn text="Save Change" important={true}/>
                <Btn text="Confirm" important={false}/>
            </div>
        )
        };
      
      const root = document.getElementById("root");
      ReactDOM.render(<App />, root);
    </script>
  </body>
</html>

부모컴포넌트 -> 자식컴포넌트로
props 통해 style 설정하기

backgroundColor, fontSize 개별 설정

  • backgroundColor: {backgroundColor} 로 안해줌!
  • 그냥 backgroundColor만 해도 됨.

backgroundColor

<Btn text="Save Change" backgroundColor="lightblue"/>
<Btn text="Confirm" backgroundColor="tomato"/>

backgroundColor backgroundColor

function Btn({ text , changeValue, backgroundColor}) {

  return (

    <button
    onClick={changeValue}
style={{
       backgroundColor,
       color: "white",
       padding: "10px 20px",
       border:0,
       borderRadius:10
      }}>{text}</button>
)
}

fontSize

<Btn text={value} changeValue={chagneValue} backgroundColor="lightblue" fontSize={20}/>

fontSize fontSize

function Btn({ text , changeValue, backgroundColor, fontSize}) {

return (

<button
        onClick={changeValue}
        style={{
        backgroundColor,
        color: "white",
        padding: "10px 20px",
        border:0,
        borderRadius:10,
        fontSize
        }}>{text}</button>
)
}



부모컴포넌트 -> 자식컴포넌트로
props 통해 fucntion 함수 보내기

📌 onClick은 이벤트리스너 (X) 그저 하나의 prop 일뿐!

  • 같은 의미로 메인컴포넌트의 customized 버튼에 style={{ color: "green" }}해도 전혀 작동 안됨.
  • 이유 : 직접 button에 style을 추가해야 함. props로 받아와서 직접 button에 속성 추가해야 적용됨.
  • 결론 : 메인컴포넌트의 props 라인에 쓰는 속성들은
    이벤트리스너가 아니라, 자식컴포넌트(Btn)로 들어가는 관문 같은 것임. 우체국 같음.

기타: (최적화 관련) React.memo()
기능: 불필요한 리렌더링 제한, Memorizing

👉 부모컴포넌트에서, props가 (상태)변화하지 않은 자식컴포넌트의 불필요한 렌더링 방지하기.
👉 props가 변화한 자식컴포넌트만 렌더링 시키기

불필요한 re-render는 React.memo()로 관리할 수 있음

  • 부모 컴포넌트의 state(상태값) 변경되면 re-render가일어나고, 당연히 그 자식 컴포넌트들도 Re-render가 일어남.
  • 문제 자식컴포넌트에 불필요한 렌더링이 발생할 수도 있음
  • 해결 React.memo()로 prop의 변경이 일어난 해당 자식컴포넌트만 리렌더링 시켜줄 수 있음.
  • 장점 부모 컴포넌트가 아주 많은 자식컴포넌트를 갖고 있는 경우 사용하면 성능과 속도 최적화에 유용.

❗리액트 개발을 하실 때, useCallback, useMemo, React.memo 는 컴포넌트의 성능을 실제로 개선할수있는 상황에서만 사용하기

❗렌더링 최적화 하지 않을 컴포넌트에 React.memo 를 사용하는것은, 불필요한 props 비교만 하는 것이기 때문에 실제로 렌더링을 방지할수있는 상황이 있는 경우에만 사용하기


예제 설명
👉 따라서, state랑 연결된 props가 변경되는 첫번째 자식컴포넌트 버튼은 리렌더링 업데이트 새로고침이 되어야 하겠지만,
👉 두번째 자식컴포넌트 버튼은 변경된 것도 없는데 굳이 리렌더링 될 필요가 없는 것임.

📌 기본적으로 리액트는 부모컴포넌트에 상태변화가 있으면, 모든 자식컴포넌트들도 리렌더링 됨.


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


예제 React.memo(자식컴포넌트)

const MemorizedBtn = React.memo(Btn)
const [value, setValue] = React.useState("Save Change")
const chagneValue = () => {
	setValue("Revert Changes")
}
<MemorizedBtn text={value} changeValue={chagneValue} />
<MemorizedBtn text="Confirm" />

React.memo(자식컴포넌트)해주면, 모든 자식들을 일괄 리렌더링 업데이트가 아니라, 상태변화된 자식컴포넌트만 리렌더링 업데이트해줌

전체코드

<!DOCTYPE html>
<html>
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone@7.17.6/babel.min.js"></script>
    <script type="text/babel">
          
        function Btn({ text , changeValue, backgroundColor}) {
            //console.log(props) // Object
            console.log(text, "was rendered")
            return (

                <button
                    onClick={changeValue}
                    style={{
                        backgroundColor,
                        color: "white",
                        padding: "10px 20px",
                        border:0,
                        borderRadius:10
                    }}>{text}</button>
            )
        }
       const MemorizedBtn = React.memo(Btn)

        function App() {
            const [value, setValue] = React.useState("Save Change")
            const chagneValue = () => {
                setValue("Revert Changes")
            }
          
        return (
            <div>
                <MemorizedBtn text={value} changeValue={chagneValue} backgroundColor="lightblue"/>
                <MemorizedBtn text="Confirm" backgroundColor="tomato"/>
            </div>
        )
        };
      
      const root = document.getElementById("root");
      ReactDOM.render(<App />, root);
    </script>
  </body>
</html>

Props Types

문제
컴포넌트가 너무 많은 props를 갖는다면?
리액트는 props 작성시 기본 문법에만 맞으면, 타입값 에러가 있어도, UI나 콘솔창에 에러를 알려주지 않음.

해결: propTypes 사용
각 props별 타입 입력값 지정, 검토 후 에러 통보

  1. script 설치
https://unpkg.com/prop-types@15.7.2/prop-types.js

  1. 코드 작성
Btn.propTypes={
           text: PropTypes.string.isRequired, 
           backgroundColor: PropTypes.string.isRequired,
           fontSize: PropTypes.number, // optional
           changeValue: PropTypes.func, // optional
           
           value1: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired// 둘중 하나 예시  // optional
           disabled: PropTypes.bool // boolean // optional
       }

위 코드의 위치는, 컴포넌트 안에 종속되지 않고 독립적으로 씀.

propTypes 사용하면, 만약 props 값 입력시 타입에러가 발생하면, 에러표시로 알려줌

.isRequired를 붙이면 필수로 해당 prop 항목이 있어야함을 의미. 없으면 에러 발생함.

그 외의 경우에는, optional임. props가 등록되지 않아도 에러발생하지 않음.

PropTypes 종류 및 디폴트 기본값 설정 : 👉클릭

import PropTypes from 'prop-types';

MyComponent.propTypes = {
  // prop가 특정 JS 형식임을 선언할 수 있습니다.
  // 이것들은 기본적으로 모두 선택 사항입니다.
  optionalArray: PropTypes.array,
  optionalBool: PropTypes.bool,
  optionalFunc: PropTypes.func,
  optionalNumber: PropTypes.number,
  optionalObject: PropTypes.object,
  optionalString: PropTypes.string,
  optionalSymbol: PropTypes.symbol,

  // 랜더링 될 수 있는 것들은 다음과 같습니다.
  // 숫자(numbers), 문자(strings), 엘리먼트(elements), 또는 이러한 타입들(types)을 포함하고 있는 배열(array) (혹은 배열의 fragment)
  optionalNode: PropTypes.node,

  // React 엘리먼트.
  optionalElement: PropTypes.element,

  // React 엘리먼트 타입 (ie. MyComponent)
  optionalElementType: PropTypes.elementType,

  // prop가 클래스의 인스턴스임을 선언할 수 있습니다.
  // 이 경우 JavaScript의 instanceof 연산자를 사용합니다.
  optionalMessage: PropTypes.instanceOf(Message),

  // 열거형(enum)으로 처리하여 prop가 특정 값들로 제한되도록 할 수 있습니다.
  optionalEnum: PropTypes.oneOf(['News', 'Photos']),

  // 여러 종류중 하나의 종류가 될 수 있는 객체
  optionalUnion: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.instanceOf(Message)
  ]),

  // 특정 타입의 행렬
  optionalArrayOf: PropTypes.arrayOf(PropTypes.number),

  // 특정 타입의 프로퍼티 값들을 갖는 객체
  optionalObjectOf: PropTypes.objectOf(PropTypes.number),

  // 특정 형태를 갖는 객체
  optionalObjectWithShape: PropTypes.shape({
    color: PropTypes.string,
    fontSize: PropTypes.number
  }),

  // 추가 프로퍼티에 대한 경고가 있는 객체
  optionalObjectWithStrictShape: PropTypes.exact({
    name: PropTypes.string,
    quantity: PropTypes.number
  }),

  // 위에 있는 것 모두 `isRequired`와 연결하여 prop가 제공되지 않았을 때
  // 경고가 보이도록 할 수 있습니다.
  requiredFunc: PropTypes.func.isRequired,

  // 모든 데이터 타입이 가능한 필수값
  requiredAny: PropTypes.any.isRequired,

  // 사용자 정의 유효성 검사기를 지정할 수도 있습니다.
  // 검사 실패 시에는 에러(Error) 객체를 반환해야 합니다.
  // `oneOfType`안에서는 작동하지 않으므로 `console.warn` 혹은 throw 하지 마세요.
  customProp: function(props, propName, componentName) {
    if (!/matchme/.test(props[propName])) {
      return new Error(
        'Invalid prop `' + propName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  },

  // `arrayOf` 와 `objectOf 에 사용자 정의 유효성 검사기를 적용할 수 있습니다.
  // 검사 실패 시에는 에러(Error) 객체를 반환해야 합니다.
  // 유효성 검사기는 배열(array) 혹은 객체의 각 키(key)에 대하여 호출될 것입니다.
  // 유효성 검사기의 첫 두 개의 변수는 배열 혹은 객체 자신과 현재 아이템의 키입니다.

  customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
    if (!/matchme/.test(propValue[key])) {
      return new Error(
        'Invalid prop `' + propFullName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  })
};

props 초기값 설정

  1. 자바스크립트 문법으로 props의 초기값 설정 가능
function Btn({ text, fontSize = 50})
  1. 리액트 defaultProps
MemorizedBtn.defaultProps={
        fontSize: 100
       }


전체 코드 (React.memo()까지 적용 완료)

<!DOCTYPE html>
<html>
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone@7.17.6/babel.min.js"></script>
    <script src="https://unpkg.com/prop-types@15.7.2/prop-types.js"></script>
    <script type="text/babel">
          
        function Btn({ text , changeValue, backgroundColor, fontSize}) {
            //console.log(props) // Object
            console.log(text, "was rendered")
            return (

                <button
                    onClick={changeValue}
                    style={{
                        backgroundColor,
                        color: "white",
                        padding: "10px 20px",
                        border:0,
                        borderRadius:10,
                        fontSize
                    }}>{text}</button>
            )
        }
       const MemorizedBtn = React.memo(Btn)

       MemorizedBtn.propTypes={
           text: PropTypes.string.isRequired, 
           backgroundColor: PropTypes.string.isRequired,
           fontSize: PropTypes.number, // optional
           changeValue: PropTypes.func // optional
       }

       MemorizedBtn.defaultProps={
        fontSize: 100
       }

        function App() {
            const [value, setValue] = React.useState("Save Change")
            const chagneValue = () => {
                setValue("Revert Changes")
            }
          
        return (
            <div>
                <MemorizedBtn text={value} changeValue={chagneValue} backgroundColor="lightblue" fontSize={20}/>
                <MemorizedBtn text="Continue" backgroundColor="tomato"/>
            </div>
        )
        };
      
      const root = document.getElementById("root");
      ReactDOM.render(<App />, root);
    </script>
  </body>
</html>






<참고>
https://ko.reactjs.org/tutorial/tutorial.html
PropTypes: https://ko.reactjs.org/docs/typechecking-with-proptypes.html

<더 알아보기>
React.memo(): https://ko.reactjs.org/docs/react-api.html#reactmemo
https://react.vlpt.us/basic/19-React.memo.html

profile
Studying Front-end Development 🌼

0개의 댓글