[TIL] 220123

Lee Syong·2022년 1월 23일
0

TIL

목록 보기
158/204
post-thumbnail

📝 오늘 한 것

  1. React props - 컴포넌트 재설정

  2. underdash 코드 리뷰 정리(2)


📚 배운 것

1. PROPS

부모 컴포넌트로부터 자식 컴포넌트에 데이터를 보낼 수 있도록 하는 방법

1) 컴포넌트 재설정

style은 같고 text만 다른 버튼들을 만들려고 한다.

(1) 비효율적인 방법

기본적으로는 다음과 같이 일일이 모든 버튼의 컴포넌트를 만들어서 style을 복붙해야 한다.
실질적으로 두 버튼 사이에 다른 점은 text 밖에 없다.

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

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

const App = () => {
  return (
    <div>
      <SaveBtn />
      <ConfirmBtn />
    </div>
  );
};

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

(2) React props 이용

그러나 React props를 이용하면 부모 컴포넌트인 App 컴포넌트에서 자식 컴포넌트인 Btn 컴포넌트에 prop을 넘겨줌으로써 하나의 버튼 컴포넌트를 재설정하는 방식으로 여러 개의 다른 컴포넌트들을 구현할 수 있다.

const Btn = (props) => { // {text, big}
  return (
    <div>
      <button
        style={{
          backgroundColor: "lightsalmon",
          color: "white",
          border: 0,
          borderRadius: 10,
          padding: "10px 20px",
          fontSize : props.big ? 18 : 16, // big ? 18 : 16
        }}
      >
        {props.text} // {text}
      </button>
    </div>
  );
};

const App = () => { // 부모 컴포넌트에서
  return (
    <div>
      <Btn text="Save" big="true" /> // 자식 컴포넌트 재설정
      <Btn text="Confirm" big="false" /> // 〃
    </div>
  );
};

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

컴포넌트는 항상 Object 객체 형식의 하나의 인자만을 받는다.
Btn 컴포넌트에서 console.log(props) 을 실행하면 {text: Save, big: true} {text: Confirm, big: false} 가 출력된다.
props 대신에 shortcut 형태인 {text , big} 을 인자로 전달할 수도 있다.

한편, 자식 컴포넌트에서는 부모 컴포넌트로부터 전달받은 Boolean 값을 가지는 데이터(big)를 바탕으로 삼항 조건 연산자를 사용할 수도 있다.

(3) props로 함수 전달

앞서 props로 string과 boolean 값을 전달했는데 함수를 전달할 수도 있다.

Btn의 text를 App의 state에 연결했다.
state가 바뀔 때마다 Btn의 text prop의 값이 바뀌도록 App 컴포넌트가 return 하는 Btn 컴포넌트에 changeValue prop을 추가한 후 Btn 컴포넌트에 인자로 changeValue prop을 전달했다.

💡 주의!

App 컴포넌트가 return 하는 커스텀 컴포넌트 Btn에 changeValue={changeValue}을 추가해도 이것은 이벤트 리스너가 아니라 Btn에 인자로 전달되는 prop일 뿐이다.
onClick={changeValue}라고 해도 마찬가지, onClick 이벤트 리스너와 동일한 이름을 갖지는 여기서의 onClick은 그저 prop일 뿐이다.

Btn 컴포넌트 안의 HTML 요소 button에 onClick={changeValue}을 추가해야 이것이 실제 이벤트 리스너가 된다.
onClick={onClick}라고 해도 마찬가지, 앞의 onClick은 이벤트 리스너, 뒤의 onClick은 prop이다.

const Btn = ({text, changeValue}) => {
  return (
    <div>
      <button
        onClick={changeValue}
        // 중략
        }}
      >
        {text}
      </button>
    </div>
  );
};

const App = () => {
  const [value, setValue] = React.useState("Save Changes");
  const changeValue = () => setValue("Revert Changes");
  return (
    <div>
      <Btn text={value} changeValue={changeValue} />
      <Btn text="Continue" />
    </div>
  );
};

(4) React.memo()

부모 컴포넌트에서 어떤 state가 변경되면 자식 컴포넌트가 부모 컴포넌트의 다른 state 값을 prop으로 사용하고 있어도 부모 컴포넌트의 state 변경에 의해 부모 컴포넌트가 리렌더링 될 때 자식 컴포넌트 또한 같이 리렌더링 된다.

이를 방지하고 컴포넌트를 기억하도록 함으로써 부모 컴포넌트의 state가 바뀌었더라도 자식 컴포넌트의 prop이 바뀌지 않는다면 리렌더링 되지 않도록 하기 위해 React.memo()를 사용할 수 있다.

const Btn = ({text, changeValue}) => {
  // 중략
};

const MemorizedBtn = React.memo(Btn); // 추가

const App = () => {
  // 중략
  return (
    <div>
      <MemorizedBtn text={value} changeValue={changeValue} /> // 수정
      <MemorizedBtn text="continue" /> // 수정
    </div>
  );
};

부모 컴포넌트의 state가 바뀔 때마다 수백 수천 개의 자식 컴포넌트가 항상 리렌더링 된다면 어플리케이션이 느려지는 원인이 될 수도 있으므로 컴포넌트의 개수가 많아진다면 이를 적절하게 사용할 필요가 있다.

(5) PropTypes

Typecheking With PropTypes 참고

하나의 컴포넌트가 매우 많은 prop을 가질 때 잘못된 prop을 전달하는 등의 문제가 발생할 수 있다.
예를 들어, text prop의 값으로 string이 아니라 number를 적어줘도 코드 상으로는 문제가 없기 때문에 그대로 적용된다.

// propType 설치 (여기선 Node.js 사용 x)
<script src="https://unpkg.com/prop-types@15.7.2/prop-types.js"></script>

propTypes 패키지는 설치하면 props가 어떤 타입이어야 하는지 설정할 수 있다.
설정한 타입과 다른 타입의 값이 입력되면 콘솔에 값이 잘못됐다고 경고가 뜬다.

// propTypes 소문자 p ❗
Btn.propTypes = {
  text: PropTypes.string.isRequired, // PropTypes 대문자 p ❗
  // isRequired를 사용하면 해당 prop이 지정되지 않으면 콘솔에 경고창을 띄워준다.
  fontSize: PropTypes.number,
};

const App = () => {
  const [value, setValue] = React.useState("Save Changes");
  const changeValue = () => setValue("Revert Changes");
  return (
    <div>
      <MemorizedBtn text={value} changeValue={changeValue} fontSize={18} />
      <MemorizedBtn text="Continue" />
    </div>
  );
};

(6) props에 기본값 매개변수 사용

자바스크립트 문법에 의해 기본값 매개변수를 사용할 수 있다.
prop의 값이 undefined인 경우 설정한 기본값이 할당된다.

const Btn = ({ text, changeValue, fontSize = 14 }) => {
  // 중략
};

2. underdash 코드 리뷰 정리(2)

1) 함수 재활용

(1) _.extend 함수

🔎 처음에 작성한 코드 (수정 전)

유사 배열인 arguments를 활용했다.
중첩 for 문이 사용되고 있다.

_.extend = function (obj) {
  for (let i = 1; i < arguments.length; i++) {
    for (key in arguments[i]) {
      obj[key] = arguments[i][key];
    }
  }
  return obj;
};

🔎 _.each 함수를 재활용하여 작성한 코드

그냥 for 반복문 대신 _.each 함수로 대체한 것뿐이다.
뭔가 개선했다기엔 너무 빈약해 보여서 다른 방법을 재시도했다.

_.extend = function (obj, ...args) {
  _.each(args, function (object) {
    _.each(object, function (value, key) {
      obj[key] = object[key];
    });
  });
}

🔎 나머지 매개변수_.each 함수, _.reduce 함수를 재활용하여 작성한 코드

  • 미리 구현해놓은 _.each 함수와 _.reduce 함수
_.each = function (collection, iterator) {
  if (Array.isArray(collection)) {
    for (let i = 0; i < collection.length; i++) {
      iterator(collection[i], i, collection);
    }
  } else {
    for (key in collection) {
      if (collection.hasOwnProperty(key)) {
        iterator(collection[key], key, collection);
      }
    }
  }
};

_.reduce = function (collection, iterator, accumulator) {
  if (accumulator !== 0 && accumulator === undefined) {
    accumulator = collection[0];

    for (let i = 1; i < collection.length; i++) {
      accumulator = iterator(accumulator, collection[i]);
    }
  } else {
    for (key in collection) {
      if (collection.hasOwnProperty(key)) {
        accumulator = iterator(accumulator, collection[key]);
      }
    }
  }

  return accumulator;
};
  • _.extend 함수 수정
_.extend = function (obj, ...args) {
  obj = _.reduce(
    args,
    function (acc, curr) {
      _.each(curr, function (value, key) {
        acc[key] = curr[key];
      });

      return acc;
    },
    obj
  );

  return obj;
};

함수 재활용을 연습 중이라 일단 수정해보긴 했는데 수정 전 코드가 더 잘 읽히는 거 같은 건 내가 내공이 부족해서일까...

(2) _.defaults 함수

_.extend 함수를 수정한 방식과 같은 방식으로 수정했다.

🔎 처음에 작성한 코드

 _.defaults = function (obj) {
    for (let i = 1; i < arguments.length; i++) {
      for (key in arguments[i]) {
        if (key in obj) {
          continue;
        }
        obj[key] = arguments[i][key];
      }
    }
    return obj;
  };

🔎 나머지 매개변수_.each 함수, _.reduce 함수를 재활용하여 작성한 코드

_.defaults = function (obj, ...args) {
  obj = _.reduce(
    args,
    function (acc, curr) {
      _.each(curr, function (value, key) {
        if (key in acc) return; // _.extend 함수에서 이 부분만 새롭게 추가함
        acc[key] = curr[key];
      });

      return acc;
    },
    obj
  );

  return obj;
};

2) 재귀 함수

재귀 함수는 명시적인 return 값을 가지는 재귀함수와 그렇지 않은 재귀 함수를 포함하여 크게 2가지로 나눌 수 있다.

(1) 명시적인 return 값을 가지지 않는 재귀 함수

function deleteItem (arr) {
  if (arr.length === 0) {
    return;
  }
  
  arr.pop();
  const newArr = arr;
  deleteItem(newArr);
}

const hi = [1, 2, 3];

deleteItem(hi);
console.log(hi); // []

(2) 명시적인 return 값을 가지는 재귀 함수

_.flatten 함수는 명시적인 return 값을 가지는 재귀 함수이다.
이 경우 재귀 함수를 실행시킨 후 그 return 값을 받아서 사용해야만 의미가 있다.

_.flatten = function (nestedArray) {
  // Hint: Use Array.isArray to check if something is an array
  const flattenedArray = [];

  for (let i = 0; i < nestedArray.length; i++) {
    if (!Array.isArray(nestedArray[i])) {
      flattenedArray.push(nestedArray[i]);
    } else {
      const innerFlattedArray = _.flatten(nestedArray[i]); // 재귀 함수 return값을 변수 innerFlattedArray에 받음
      flattenedArray.push(...innerFlattedArray); // 그 값을 사용하는 코드
    }
  }

  return flattenedArray;
};

✨ 내일 할 것

  1. CREATE REACT APP

  2. underdash 코드 최종 정리 후 commit & push

profile
능동적으로 살자, 행복하게😁

0개의 댓글