prerequisite

+ composition vs inheritance

highscore 만들기

점수가 가장 높은 사용자가 있을 겨우 player 이름 왼쪽에 왕관을 나타나게 한다. 기존의 player 컴포넌트에 새로운 기능을 추가해서 새로운 컴포넌트를 만들기 위해서 상속을 사용하는 것보다 컴포지션 기법을 사용한다.

Player + 새로운 기능1 = CustomPlayer1

Player + 새로운 기능2 = CustomPlayer2

Player + 새로운 기능3 = CustomPlayer3

이런식으로 확장을 한다. 기존 Player 컴포넌트에 왕관을 나타내는 새로운 기능을 추가한 확장형 컴포넌트를 만들려는 것이고, 리액트에서는 이것을 composition 기법으로 처리하고 vue 나 다른 라이브러리에서는 slot 이라는 개념을 사용한다.

CustomPlayer 컴포넌트를 새로 생성하고 Player 컴포넌트를 컴포지션 기법으로 포함시킨다.

Player 컴포넌트에 children 속성으로 왕관을 svg 벡터 그래픽으로 삽입하였다. 부모로 부터 isHighScore 속성이 true 혹은 false로 boolean 속성으로 넘어오고 이 속성을 클래스에 동적으로 바인딩한다.

is-high-score 클래스 속성은 0.35초 동안 1.4배 커졌다가 다시 원래 크기로 돌아오는 애니메이션이다.

또한 모든 속성을 자식에게 그대로 넘겨주기 위해서 { …props} 를 사용한것은 자주 사용되니 유심히 보자.

App 컴포넌트에 isHighScore 속성을 계산해서 넘겨준다. 타입은 boolean 이다.

score의 max 값을 구하기 위해서 lodash의 maxBy 함수를 사용하였다. maxBy를 사용해서 리턴되는 형태는 object 형태이다. 앞에서 sumBy를 사용해서 리턴된 값은 primitive 였지만 여기서는 object 이므로 해당 값을 접근해서 추출해야 한다.

또한, Player 컴포넌트가 아니라 Player컴포넌트를 확장한 CustomPlayer에게 넘겨주어야 한다.

Player 컴포넌트에는 삭제 버튼 바로 아래에 부모에서 전달된 children을 그대로 넣어준다.

지금까지, composition 기법을 사용해서 Player + 왕관 기능을 추가한 CustomPlayer를 구현해보았다.

동적 클래스 바인딩

is-high-score 와 같이 동적으로 특정 클래스를 넣거나 빼거나 하는 경우가 개발시에 많이 필요하다.

vueJS, angular 에서는 클래스 바인딩이라는 템플릿 문법이 따로 만들어져서 쉽게 바인딩이 가능한데 리액트에서는 해당 문법이 없기 때문에 위에서 한것과 같이 자바스크립트로 매핑하다보니 실제 코드가 3-4줄이 더 필요했다.

리액트 튜토리얼을 보면 리액트에서는 제공하지 않지만 3rd party 라이브러리를 사용하라는 가이드가 나오는데 해당 라이브러리가 classnames 라는 라이브러리이다.

해당 라이브러리를 인스톨한다.

npmjs.com 에 가서 이 라이브러리를 검색해보면 다운로드 수가 리액트와 거의 맞먹는 수준으로 개발시 거의 필수 라이브러리라고 할 수 있다.

사용법은 import 후에 함수안에 넣어야 할 클래스명을 정적으로 혹은 동적으로 넣을수 있다.

  • classNames(‘svg’) => svg 클래스명이 추가. 스트링 형태로 사용 가능
  • classNames({‘svg’: true}) => json 형태로 사용 가능하다. true이면 svg 클래스명이 추가
  • classNames({‘svg’: false}) => false이므로 svg 클래스명이 추가되지 않는다.
  • classNames({‘is-high-score’: props.isHighScore}) => props 값에 true이면 is-highScore 가 추가되고 false 이면 추가되지 않는다. 이것을 동적 클래스 바인딩이라고 한다.
  • classNames({‘svg’, true, ‘is-high-score’: props.isHighScore}) => json에 여러개를 넣을 수 있다.
  • classNames(‘svg’, {‘is-high-score’: props.isHighScore}) => ,로 분리해서 여러개로도 사용가능하다.