③ 숫자야구

hhkim·2020년 1월 25일
0
post-thumbnail

🔗 웹 게임을 만들며 배우는 React

1. import와 require 비교

  • 서로 전환 가능
const React = require('react');
import React from 'react';
  • 여러 모듈을 동시에 import 할 수도 있음
import React, { Component } from 'react';
  • export도 다름
    - export: 여러 번 사용 가능
    - export default: 한 번만 사용 가능
module.exports = NumberBaseball;	// export default와 비슷
export.NumberBaseball = 'NumberBaseball';	// == module.export = NumberBaseball;
// 위가 require, 아래는 import와 같은 문법
export default NumberBaseball;	// import NumberBaseball;
export const NumberBaseball = 'NumberBaseball';	// import { NumberBaseball }
  • node에서는 기본적으로 require를 지원하지만, babel이 import를 require로 바꿔주어 둘다 사용 가능
    👉 node에서는 require를, babel에서는 import를 사용한다.

2. 리액트 반복문(map)

💡
valueonChange는 짝
👉 defaultValue로 한번에 쓸 수도 있다.

<input value={this.state.value} onChange={this.onChangeInput} />
<input defaultValue={this.state.value} />

💡
화살표 함수를 사용하면

  • constructor를 사용하지 않아도 된다.
  • 가독성이 높아진다.
  • 반복되는 부분을 배열로 만들어서 map함수로 사용

<ul>
    {['사과', '바나나', '포도', '귤', '감', '배', '파인애플'].map((v) => {
      return (
        <li>{v}</li>
      );
    })}
</ul>

3. 리액트 반복문(key)

  • react는 key를 보고 컴포넌트를 구분
    👉 반복문에서 고유한 key값을 부여하지 않으면 오류 발생

<ul>
    {[
      {fruit: '사과', taste: '맛있다'}, 
      {fruit: '바나나', taste: '맛없다'},
      {fruit: '포도', taste: '떫다'},
      {fruit: '귤', taste: '시다'}, 
      {fruit: '감', taste: '달다'}
    ].map((v) => {
      return (
        <li key={v.fruit}><b>{v.fruit}</b> - {v.taste}</li>
      );
    })}
</ul>
  • i로 인덱스 값을 가져올 수도 있지만, 성능 문제 때문에 key로 사용하지 않는 것이 좋음

💡 key가 필요한 이유 / 인덱스 key가 별로인 이유

  • 리액트에서는 key 값을 기준으로 엘리먼트를 추가, 수정, 삭제
  • 따로 지정하지 않으면 자동으로 인덱스 값으로 key가 정해짐
  • 인덱스를 key로 사용하다가 배열의 순서가 바뀌면 인덱스도 다시 정해져서 성능 저하
  • return도 생략 가능 (중괄호 없이 소괄호나 소괄호 없이 바로 리턴값)
{[...].map((v, i) => 
    <li key={v.fruit}><b>{v.fruit}</b> - {v.taste}</li>
)}

4. 컴포넌트의 분리와 props

  • 반복문의 가독성, 재사용성과 성능을 위해 컴포넌트를 따로 분리하는 것이 좋음
  • props: 컴포넌트 사이에 서로 전달되는 값
    - 부모-자식 관계 형성
    • 부모-자식 관계가 복잡해지는 경우에 context, redux 등이 사용됨

NumberBaseball.jsx

import Try from './Try';
...
{this.fruits.map((v, i) => {
    return (
      <Try key={v.fruit + v.taste} value={v} index={i} /> <!-- props 전달 -->
    );
})}

Try.jsx

import React, { Component } from 'react';

class Try extends Component {
    render() {
     return (
        <li>
            <b>{this.props.value.fruit}</b> - {this.props.index}
        </li>
     );   
    }
}

export default Try;

5. 주석과 메서드 바인딩

1) 주석

{/*  내용  */}

2) 메서드 바인딩

  • 예전에는 constructor에 state, 함수를 선언했지만, 최근에는 constructor 없이 state를 선언하고 화살표 함수를 사용

6. 숫자야구 만들기

  • 리액트에서 배열은 push를 사용하지 않고 복사하는 방식으로 변경
    👉 변경을 감지하지 못하여 render가 되지 않기 때문
// [...복사할 배열, 새로운 배열 선언]
tries: [...prevState.tries, {try: this.state.value, result: '홈런!'}]

7. Q&A

  • this를 사용하지 않으면 함수를 클래스 밖에 선언하고 사용 가능
  • map(): - 배열을 일대일로 짝짓는 함수
[1, 2, 3].map((v) => (v * 2));	// [2, 4, 6]

8. 숫자야구 Hooks로 전환하기

  • 이전 값이 필요할 때는 함수형으로 사용
setTries((prevTries) =>  [...prevTries, {try: value, result: '홈런!'}]);

9. React Devtools

  • props를 사용하면 렌더링이 자주 일어나서 성능 저하
    👉 React Devtools를 사용하여 이런 문제가 언제 일어나고 어떻게 해결할 수 있는지 알 수 있다.
  • 크롬 - 확장프로그램 - React Developer Tools 설치
  • 배포된 리액트 앱에서는 파란 불이 들어옴
  • 배포 모드에서는 소스 코드 압축 및 최적화가 되어 있음
    • 배포 모드로 변경하는 방법
      👉 webpack.config.js를 아래와 같이 변경

10. shouldComponentUpdate

  • React - Component - 설정(톱니바퀴)
  • 렌더링이 될 때마다 깜빡거림
    • 변경되는(깜빡이는) 부분의 색이 붉을수록 변경되는 부분이 많으므로 성능 저하
  • 실제로 state가 변경되지 않아도 setState()가 호출되기만 하면 렌더링이 다시 일어남
    👉 shouldComponentUpdate()를 사용해서 렌더링이 일어나는 경우를 제한
shouldComponentUpdate(nextProps, nextState, nextContext){
  	// 현재값과 변경하려는 값이 다를 경우에만 true 리텅 (렌더링)
        if(this.state.counter !== nextState.counter){
            return true;
        }
        return false;
    }

11. PureComponent와 React.memo

1) PureComponent

  • shouldComponentUpdate를 구현해둔 Component
  • 일반 자료형과 달리 객체나 배열 등 참조형은 원래 값이 변경되어 비교가 제대로 되지 않을 수 있음
    👉 아래와 같이 복사해서 사용
array: [...this.state.array, 1]);

state에 객체 구조를 쓰지 않는 것이 좋다.

2) React.memo

  • Hooks에서 사용하는 렌더링 제한
import React, {memo}  from 'react';

const Try = memo(({ tryInfo }) => {   // 컴포넌트를 memo로 감싸줌
   return (
      <li>
         <div>{tryInfo.try}</div>
         <div>{tryInfo.result}</div>
      </li>
   );
});

export default Try;


가장 아래에 있는 자식에 PureComponent나 memo를 적용해야 하며, 모든 자식에 적용이 된 경우 부모 컴포넌트에도 사용 가능

12. React.createRef

  • createRef를 사용하면 Class, Hooks 상관없이 current로 접근 가능
import React, { useState, createRef } from 'react';

const inputRef = createRef();

inputRef.current.focus();	// 접근

<input ref={inputRef} maxLength={4} value={value} onChange={onChangeInput} />
  • 부가적인 동작을 넣고 싶을 때는 기존의 함수형 방식을 사용
this.inputRef.focus();	// 접근

inputRef;
onInputRef = (c) => {
  console.log('포커스');
  this.inputRef = c;
}

<input ref={this.onInputRef} maxLength={4} value={value} onChange={onChangeInput} />

13. props와 state 연결하기


render() 안에 setState()를 사용하면 무한반복이 발생하므로 주의!

  • props는 자식이 아닌 부모에서 변경이 일어나야 함
    👉 자식에서 변경이 필요한 경우 props와 state를 연결하여 사용
import React, {memo, useState}  from 'react';

const Try = memo(({ tryInfo }) => {   
   const [result, setResult] = useState(tryInfo.result);	// props와 state 연결

   const onClick = () => {
      setResult('1');	// state 변경
   }
   
   return (
      <li>
         <div>{tryInfo.try}</div>
         <div onClick={onClick}>{tryInfo.result}</div>
      </li>
   );
});

0개의 댓글