티스토리 뷰

3.1 클래스형 컴포넌트

컴포넌트를 선언하는 방식은 두 가지이다. 하나는 함수 컴포넌트이고, 또 다른 하나는 클래스형 컴포넌트이다.

클래스형 컴포넌트는 다음과 같이 이루어져 있다.

import React, { Component } from "react";

class App extends Component {
  render() {
    const name = "react";
    return <div className="react">{name}</div>;
  }
}

export default App;

클래스형 컴포넌트로 바뀌었지만 역할을 함수 컴포넌트와 똑같다. 클래스형 컴포넌트와 함수 컴포넌트의 차이점은 클래스형 컴포넌트의 경우 state 기능 및 라이프사이클 기능을 사용할 수 있다는 것과 임의 메서드를 정의할 수 있다는 것이다.

ES6의 클래스 문법
ES6 이전에는 자바스크립트에 클래스(class)가 없었다. 개념 자체는 있었지만, 그것을 구현하려면 class대신에 prototype이라는 문법을 사용하여 다음과 같이 작업해야 했다.
function Dog(name) {
  this.name = name;
}

Dog.prototype.say = function() {
  console.log(this.name + ': 멍멍');
}
var dog = new Dog('검둥이');
dog.say(); // 검둥이: 멍멍

// ES6 문법부터는 이것과 기능이 똑같은 코드를 CLASS를 사용하여 다음과 같이 작성할 수 있다.
class Dog {
  constructor(name) {
    this.name = name;
  }
  say() {
    console.log(this.name + ": 멍멍");
  }
}

const dog = new Dog("흰둥이");
dog.say(); // 흰둥이: 멍멍

클래스형 컴포넌트에서는 render 함수가 곡 있어야 하고, 그 안에서 보여 주어야 할 JSX를 반환해야 한다.

 

컴포넌트를 선언할 수 있는 두 가지 방법 중 어느 상황에 함수 컴포넌트를 사용해야 할까?

 

함수 컴포넌트의 장점

클래스형 컴포넌트보다 선언하기가 편하다.

메모리 자원도 클래스형 컴포넌트보다 덜 사용한다.

프로젝트를 완성하여 빌드한 후 배포할 때도 함수 컴포넌트를 사용하는 것이 결과물의 파일 크기가 더 작다.

함수 컴포넌트와 클래스형 컴포넌트는 성능과 파일 크기 면에서 사실상 별 차이가 없으므로 이 부분은 중요하지 않다.

 

함수 컴포넌트의 단점

state와 라이프사이클 API의 사용이 불가능하다.이 단점은 리액트 v16.8 업데이트 이후 Hooks라는 기능이 도입되면서 해결되었다.완전히 클래스형 컴포넌트와 똑같이 사용할 수 있는 것은 아니지만 조금 다른 방식으로 비슷한 작업을 할 수 있게 되었다.

 

리액트 공식 매뉴얼에서는 컴포넌트를 새로 작성할 때 함수 컴포넌트와 Hooks를 사용하도록 권장하고 있다. 하지만 그렇다고 해서 클래스형 컴포넌트가 사라지는 것은 아니므로 클래스형 컴포넌트의 기능은 알아두어야 한다.

3.2 첫 컴포넌트 생성

파일 만들기 > 코드 작성하기 > 모듈 내보내기 및 불러오기

3.2.1 src 디렉터리에 MyComponent.js 파일 생성

컴포넌트를 만들려면 컴포넌트 코드를 선언해야 한다. 

3.2.2 코드 작성하기

const MyComponent = () => {
  return <div>나의 새롭고 멋진 컴포넌트</div>;
};

export default MyComponent;

이번에 작성한 코드는 이전에 보았던 App 컴포넌트와 형태가 조금 다르다. 함수를 작성할 때 function 키워드를 사용하는 대신에 () => {}를 사용하여 함수를 만들어 주었다. 이는 ES6에 도입된 화살표 함수 문법이다.

ES6의 화살표 함수
화살표 함수는 ES6 문법에서 함수를 표현하는 새로운 방식이다. 그렇다고 해서 기존 function을 이용한 함수 선언 방식을 아예 대체하지는 않는다. 사용 용도가 조금 다르다. 이 문법은 주로 함수를 파라미터로 전달할 때 유용하다.
setTimeout(function () {
  console.log("hello world");
}, 1000);

setTimeout(() => {
  console.log("hello world");
}, 1000);

// 이 문법이 기존 function을 대체할 수 없는 것은 용도가 다르기 때문이다. 가리키고 있는 this 값이 다르다.
function BlackDog() {
  this.name = "흰둥이";
  return {
    name: "검둥이",
    bark: function () {
      console.log(this.name + ": 멍멍!");
    },
  };
}

const blackDog = new BlackDog();
blackDog.bark(); // 검둥이: 멍멍!

function WhiteDog() {
  this.name = "흰둥이";
  return {
    name: "검둥이",
    bark: () => {
      console.log(this.name + ": 멍멍!");
    },
  };
}
const whiteDog = new WhiteDog();
whiteDog.bark(); // 흰둥이: 멍멍!

// function()을 사용했을 때는 검둥이가 나타나고 ()=>를 사용했을 때는 흰둥이가 나타난다.
// 일반 함수는 자신이 종속된 객체를 this로 가리키며, 화살표 함수는 자신이 종속된 인스턴스를 가리킨다.
//화살표 함수는 값을 연산하여 바로 반환해야 할때 사용하면 가독성을 높일 수 있다.
function twice(value){
  return value *2;
}

const triple = (value) => value *3;

// 이렇게 따로 {}을 열어 주지 않으면 연산한 값을 그대로 반환한다는 의미이다.

함수 컴포넌트를 선언할 때 function 키워드를 사용하는 걳과 화살표 함수 문법을 사용하는 것간에는 큰 차이가 없다.

React Code Snippet을 사용하여 코드 생성하기
VS Code에서 Reactjs Code Snippet 확장 프로그램을 설치했다면 컴포넌트 코드를 간편하고 빠르게 생성할 수 있다. 
에디터에서 rsc를 입력하고 enter를 누른다.
코드를 생성하고 나면 컴포넌트 이름이 선택된 상태로 나타나는데, 여기서 수정하면 컴포넌트 이름을 변경할 수 있고 Tab을 누르면 선택을 해제할 수 있다. 클래스형 컴포넌트는 rcc를 입력하여 사용할 수 있다.

3.2.3 모듈 내보내기 및 불러오기

3.2.3.1 모듈 내보내기(export)

export default Mycomponent;

이 코드는 다른 파일에서 이 파일을 import할 때, 위에서 선언한 Mycomponent 클래스를 불러오도록 설정한다.

3.2.3.2 모듈 불러오기(import)

import MyComponent from "./MyComponent";

const App = () => {
  return <MyComponent />;
};

export default App;

위 코드에서 import 구문을 사용하는 두 번째 줄은 Mycomponent 컴포넌트를 불러온다.

3.3 props

props는 properties를 줄인 표현으로 컴포넌트 속성을 설정할 때 사용하는 요소이다. props 값은 해당 컴포넌트를 불러와 사용하는 부모 컴포넌트에서 설정할 수 있다.

3.3.1 JSX 내부에서 props 렌더링

props 값은 컴포넌트 함수의 파라미터로 받아 와서 사용할 수 있다. props를 렌더링할 때 JSX 내부에서 {}기호로 감싸 주면 된다.

const MyComponent = (props) => {
  return <div>안녕하세요, 제 이름은 {props.name}입니다.</div>;
};

export default MyComponent;

3.3.2 컴포넌트를 사용할 때 props 값 지정하기

App 컴포넌트에서 MyComponent의 props 값을 지정한다.

import MyComponent from "./MyComponent";

const App = () => {
  return <MyComponent name="react" />;
};

export default App;

3.3.3 props 기본값 설정: defaultProps

props 값을 따로 지정하지 않았을 때 보여 줄 기본 값을 설정한다.

const MyComponent = (props) => {
  return <div>안녕하세요, 제 이름은 {props.name}입니다.</div>;
};

MyComponent.defaultProps = {
  name: "기본 이름",
};

export default MyComponent;

3.3.4 태그 사이의 내용을 보여주는 children

리액트 컴포넌트를 사용할 때 컴포넌트 태그 사이의 내용을 보여 주는 props이다.

import MyComponent from "./MyComponent";

const App = () => {
  return <MyComponent>리액트</MyComponent>;
};

export default App;

위 코드에서 MyComponent 태그 사이에 작성한 리액트라는 문자열을 MyComponent 내부에서 보여 주려면 props.children 값을 보여 주어야 한다.

const MyComponent = (props) => {
  return (
    <div>
      안녕하세요, 제 이름은 {props.name}입니다.childeren 값은 {props.children}
      입니다.
    </div>
  );
};

MyComponent.defaultProps = {
  name: "기본 이름",
};

export default MyComponent;

3.3.5 비구조화 할당 문법을 통해 props 내부 값 추출하기

myComponent에서 props 값을 조회할 때마다 props.name, props.children과 같이 props.이라는 키워드를 앞에 붙여주는데, 이 작업을 더 편하게 하기 위해 ES6의 비구조화 할당 문법을 사용하여 내부 값을 바로 추출할 수 있다.

const MyComponent = (props) => {
  const { name, children } = props;
  return (
    <div>
      안녕하세요, 제 이름은 {name}입니다.childeren 값은 {children}
      입니다.
    </div>
  );
};

MyComponent.defaultProps = {
  name: "기본 이름",
};

export default MyComponent;

이렇게 코드를 작성하면 name 값과 children 값을 더 짧은 코드로 사용할 수 있다.

객체에서 값을 추출하는 문법을 비구조화 할당(구조분해 문법)이라고 부른다. 함수의 파라미터 부분에서도 사용할 수 있다. 함수의 파라미터가 객체라면 그 값을 바로 비구조화해서 사용하는 것이다.

const MyComponent = ({ name, children }) => {
  return (
    <div>
      안녕하세요, 제 이름은 {name}입니다.childeren 값은 {children}
      입니다.
    </div>
  );
};

MyComponent.defaultProps = {
  name: "기본 이름",
};

export default MyComponent;

 3.3.6 propTypes를 통한 props 검증

컴포넌트의 필수 props를 지정하거나 props의 타입을 지정할 때는 propTypes를 사용한다. 컴포넌트의 propTypes를 지정하는 방법은 defaultProp을 설정하는 것과 비슷하다. 우선 propTypes를 사용하려면 코드 상단에 import 구문을 사용하여 불러와야 한다.

import PropsTypes from "prop-types";

const MyComponent = ({ name, children }) => {
  return (
    <div>
      안녕하세요, 제 이름은 {name}입니다.childeren 값은 {children}
      입니다.
    </div>
  );
};

MyComponent.defaultProps = {
  name: "기본 이름",
};

MyComponent.PropsTypes = {
  name: PropsTypes.string,
};

export default MyComponent;

이렇게 설정해 주면 name 값은 무조건 문자열 형태로 전달해야 된다는 것을 의미한다. App 컴포넌트에서 name 값을 문자열이 아닌 숫자로 전달한 뒤 개발자 도구의 Console탭을 확인한다.

import MyComponent from "./MyComponent";

const App = () => {
  return <MyComponent name={3}>리액트</MyComponent>;
};

export default App;

만약 컴포넌트가 설정한 props가 propTypes에서 지정한 형태와 일치하지 않는다면 브라우저 개발자 도구의 Console탭에 다음과 같은 결과가 나타난다.

값은 나타나긴했지만, 콘솔에 경고 메시지를 출력하여 개발자에게 propTypes가 잘못되었다는 것을 알려준다. 오류 메시지를 확인했다면 name 값을 제대로 설정해 주어야 한다.

3.3.6.1 isRequired를 사용하여 필수 propTypes 설정

propTypes를 지정하지 않았을 때 경고 메시지를 띄워 준다. propTypes를 지정할 때 뒤에 isRequired를 붙여주면 된다.

import PropTypes from "prop-types";

const MyComponent = ({ name, favoriteNumber, children }) => {
  return (
    <div>
      안녕하세요, 제 이름은 {name}입니다.
      <br />
      childeren 값은 {children}
      입니다.
      <br />
      제가 좋아하는 숫자는 {favoriteNumber}입니다.
    </div>
  );
};

MyComponent.defaultProps = {
  name: "기본 이름",
};

MyComponent.propTypes = {
  name: PropTypes.string,
  favoriteNumber: PropTypes.number.isRequired,
};

export default MyComponent;

코드를 저장하고 다시 개발자 도구의 Console을 확인해보면 아직 favoriteNumber를 설정하지 않았기 때문에 다음과 같은 경고가 나타난다.

 

경고를 확인했다면 favoriteNumber 값을 제대로 전달한다.

import MyComponent from "./MyComponent";

const App = () => {
  return (
    <MyComponent name="React" favoriteNumber={1}>
      리액트
    </MyComponent>
  );
};

export default App;

 3.3.6.2 더 많은 PropTypes 종류

• array: 배열
• arrayOf(다른 PropType): 특정 PropType으로 이루어진 배열을 의미합니다. 예를 들어 arrayOf(PropTypes.number)는 숫자로 이루어진 배열입니다.
• bool: true 혹은 false 값
• func: 함수
• number: 숫자
• object: 객체
• string: 문자열
• symbol: ES6의 Symbol
• node: 렌더링할 수 있는 모든 것(숫자, 문자열, 혹은 JSX 코드. children도 node PropType입니다.)
• instanceOf(클래스): 특정 클래스의 인스턴스(예: instanceOf(MyClass))
• oneOf(['dog', 'cat']): 주어진 배열 요소 중 값 하나
• oneOfType([React.PropTypes.string, PropTypes.number]): 주어진 배열 안의 종류 중 하나
• objectOf(React.PropTypes.number): 객체의 모든 키 값이 인자로 주어진 PropType인 객체

• shape({ name: PropTypes.string, num: PropTypes.number }): 주어진 스키마를 가진 객체
• any: 아무 종류
더 자세한 정보는 https://github.com/facebook/prop-types에서 확인할 수 있다.

3.3.7 클래스형 컴포넌트에서 props 사용하기

클래스형 컴포넌트에서 props를 사용할 때는 render 함수에서 this.props를 조회하면 된다. 그리고 defaultProps와 propTypes는 똑같은 방식으로 설정할 수 있다. 

import { Component } from "react";
import PropTypes from "prop-types";

class MyComponent extends Component {
  render() {
    const { name, favoriteNumber, children } = this.props;
    return (
      <div>
        안녕하세요, 제 이름은 {name}입니다.
        <br />
        childeren 값은 {children}
        입니다.
        <br />
        제가 좋아하는 숫자는 {favoriteNumber}입니다.
      </div>
    );
  }
}

MyComponent.defaultProps = {
  name: "기본 이름",
};

MyComponent.propTypes = {
  name: PropTypes.string,
  favoriteNumber: PropTypes.number.isRequired,
};

export default MyComponent;

클래스형 컴포넌트에서 defaultProps와 propTypes를 설정할 때 class 내부에서 지정하는 방법도 있다.

import { Component } from "react";
import PropTypes from "prop-types";

class MyComponent extends Component {
  static defaultProps = {
    name: "기본 이름",
  };

  static propTypes = {
    name: PropTypes.string,
    favoriteNumber: PropTypes.number.isRequired,
  };
  render() {
    const { name, favoriteNumber, children } = this.props;
    return (
      <div>
        안녕하세요, 제 이름은 {name}입니다.
        <br />
        childeren 값은 {children}
        입니다.
        <br />
        제가 좋아하는 숫자는 {favoriteNumber}입니다.
      </div>
    );
  }
}

export default MyComponent;
defaultProps와 propTypes는 꼭 사용해야 하나요?
이 두 가지 설정은 컴포넌트의 필수 사항이 아니므로 꼭 사용할 필요가 없다. 하지만 React를 사용하여 큰 규모의 프로젝트를 진행한다면, 특히 다른 개발자와 협업한다면 해당 컴포넌트가 어떤  props가 필요한지 쉽게 알 수 있어 개발 능률이 좋아진다.

3.4 state

리액트에서 state는 컴포넌트 내부에서 바뀔 수 있는 값을 의미한다. props는 컴포넌트가 사용되는 과정에서 부모 컴포넌트가 설정하는 값이며, 컴포넌트 자신은 해당 props를 읽기 전용으로만 사용할 수 있다. props를 바꾸려면 부모 컴포넌트에서 바꾸어 주어야 한다.

 

리액트에는 두 가지 종류의 state가 있다. 하나는 클래스형 컴포넌트가 지니고 있는 state이고, 다른 하나는 함수 컴포넌트에서 useState라는 함수를 통해 사용하는 state이다.

3.4.1 클래스형 컴포넌트의 state

import React, { Component } from "react";

class Counter extends Component {
  constructor(props) {
    super(props);
    // state의 초기값 설정하기
    this.state = {
      number: 0,
    };
  }
  render() {
    const { number } = this.state; // state를 조회할 때는 this.state로 조회한다.
    return (
      <div>
        <h1>{number}</h1>
        <button
          // onClick을 통해 버튼이 클릭되었을 때 호출할 함수를 지정한다.
          onClick={() => {
            // this.setState를 사용하여 state에 새로운 값을 넣을 수 있다.
            this.setState({ number: number + 1 });
          }}
        >
          +1
        </button>
      </div>
    );
  }
}

export default Counter;

constructor 

컴포넌트에 state를 설정할 때는 constructor 메서드를 작성하여 설정한다.

이는 컴포넌트 생성자 메서드인데, 클래스형 컴포넌트에서 constructor를 작성할 때는 반드시 super(props)를 호출해 주어야 한다. 이 함수가 호출되면 현재 클래스형 컴포넌트가 상속받고 있는 리액트의 Component 클래스가 지닌 생성자 함수를 호출해 준다.

그 다음에는 this.state 값에 초기값을 설정해 준다. 컴포넌트의 state는 객체 형식이어야 한다.

 

render

render 함수에서 현재 state를 조회할 때는 this.state를 조회하면 된다. button 안에 onClick이라는 값은 props로 넣어 주었는데 이는 버튼이 클릭될 때 호출시킬 함수를 설정할 수 있게 해준다.(이벤트를 설정한다.)

 

화살표 함수

이벤트로 설정할 함수를 넣어 줄 때는 화살표 함수 문법을 사용하여 넣어 주어야 한다. 함수 내부에서는 this.setState라는 함수를 사용하는데, 이 함수가 state 값을 바꿀 수 있게 해준다.

 

3.4.1.1 state 객체 안에 여러 값이 있을 때

state 객체 안에는 여러 값이 있을 수 있다. 

import React, { Component } from "react";

class Counter extends Component {
  constructor(props) {
    super(props);
    // state의 초기값 설정하기
    this.state = {
      number: 0,
      fixNumber: 0,
    };
  }
  render() {
    const { number, fixNumber } = this.state; // state를 조회할 때는 this.state로 조회한다.
    return (
      <div>
        <h1>{number}</h1>
        <h2>바뀌지 않는 값: {fixNumber}</h2>
        <button
          // onClick을 통해 버튼이 클릭되었을 때 호출할 함수를 지정한다.
          onClick={() => {
            // this.setState를 사용하여 state에 새로운 값을 넣을 수 있다.
            this.setState({ number: number + 1 });
          }}
        >
          +1
        </button>
      </div>
    );
  }
}

export default Counter;

3.4.1.2 state를 constructor에서 꺼내기

이렇게 하면 constructor 메서드를 선언하지 않고도 state 초깃값을 설정할 수 있다.

import React, { Component } from "react";

class Counter extends Component {
  state = {
    number: 0,
    fixNumber: 0,
  };
  render() {
    const { number, fixNumber } = this.state; // state를 조회할 때는 this.state로 조회한다.
    return (
      <div>
        <h1>{number}</h1>
        <h2>바뀌지 않는 값: {fixNumber}</h2>
        <button
          // onClick을 통해 버튼이 클릭되었을 때 호출할 함수를 지정한다.
          onClick={() => {
            // this.setState를 사용하여 state에 새로운 값을 넣을 수 있다.
            this.setState({ number: number + 1 });
          }}
        >
          +1
        </button>
      </div>
    );
  }
}

export default Counter;

3.4.1.3 this.setState에 객체 대신 함수 인자 전달하기

this.setState를 사용하여 state 값을 업데이트할 때는 상태가 비동기적으로 업데이트된다. 

onClick={() => {
            // this.setState를 사용하여 state에 새로운 값을 넣을 수 있다.
            this.setState({ number: number + 1 });
            this.setState({ number: this.state.number + 1 });
          }}

코드를 위와 같이 작성하면 this.setState를 두 번 사용하는 것임에도 불구하고 버튼을 클릭할 때 숫자가 1씩 더해진다.this.setState를 사용한다고 해서 state 값이 바로 바뀌지는 않기 때문이다.

 

이에 대한 해결책은 this.setState를 사용할 때 객체 대신에 함수를 인자로 넣어 주는 것이다. 

this.setState((prevState, props)=>{
  return {
    // 업데이트하고 싶은 내용
  }
})

여기서 prevState는 기존 상태이고, props는 현재 지니고 있는 props를 가리킨다. 만약 업데이트하는 과정에서 props가 필요하지 않다면 생략해도 된다.

import React, { Component } from "react";

class Counter extends Component {
  state = {
    number: 0,
    fixNumber: 0,
  };
  render() {
    const { number, fixNumber } = this.state; // state를 조회할 때는 this.state로 조회한다.
    return (
      <div>
        <h1>{number}</h1>
        <h2>바뀌지 않는 값: {fixNumber}</h2>
        <button
          // onClick을 통해 버튼이 클릭되었을 때 호출할 함수를 지정한다.
          onClick={() => {
            this.setState((prevState) => {
              return {
                number: prevState.number + 1,
              };
            });
            // 위 코드와 아래 코드는 완전히 똑같은 기능을 한다.
            // 아래 코드는 함수에서 바로 객체를 반환한다는 의미이다.
            this.setState(prevState =>({
              number: prevState.number + 1
            }))
          }}
        >
          +1
        </button>
      </div>
    );
  }
}

export default Counter;

화살표 함수에서 값을 바로 반환하고 싶다면 코드 블록 {}를 생략하면 된다. 

const sum = (a, b) => a + b;

onClick에서 두 번째로 this.setState 함수를 사용할 때는 화살표 함수에서 바로 객체를 반환하도록 했기 때문에 prevState => ({})와 같은 형태로 코드가 이루어진다.

3.4.1.4 this.setState 

setState를 사용하여 값을 업데이트하고 난 다음에 특정 작업을 하고 싶을 때는 setState의 두 번째 파라미터로 콜백(callback) 함수를 등록하여 작업을 처리할 수 있다.

import React, { Component } from "react";

class Counter extends Component {
  state = {
    number: 0,
    fixNumber: 0,
  };
  render() {
    const { number, fixNumber } = this.state; // state를 조회할 때는 this.state로 조회한다.
    return (
      <div>
        <h1>{number}</h1>
        <h2>바뀌지 않는 값: {fixNumber}</h2>
        <button
          // onClick을 통해 버튼이 클릭되었을 때 호출할 함수를 지정한다.
          onClick={() => {
            this.setState(
              {
                number: number + 1,
              },
              () => {
                console.log("방금 setState가 호출되었습니다");
                console.log(this.state);
              }
            );
          }}
        >
          +1
        </button>
      </div>
    );
  }
}

export default Counter;

3.4.2 함수 컴포넌트에서 useState 사용하기

리액트 16.8 이전 버전에서는 함수 컴포넌트에서 state를 사용할 수 없었다.하지만 16.8 이후부터는 useState라는 함수를 사용하여 함수 컴포넌트에서도 state를 사용할 수 있게 되었다.

3.4.2.1 배열 비구조화 할당

배열 비구조화 할당은 이전에 배운 객체 비구조화 할당과 비슷하다. 즉, 배열 안에 들어 있는 값을 쉽게 추출할 수 있도록 해 주는 문법이다.

const array = [1, 2];
const one = array[0];
const two = array[1];

arry 안에 있는 값을 one과 two에 담아 주는 코드이다. 배열 비구조화 할당을 사용하면 다음과 같이 표현할 수 있다.

const array = [1, 2];
const [one, two] = array;

3.4.2.2 useState 사용하기

useState 함수의 인자에는 상태의 초깃값을 넣어 준다. 클래스형 컴포넌트에서의 state 초깃값은 객체 형태를 넣어 주어야 한다. useState에서는 반드시 객체가 아니어도 상관없다. 값의 형태는 자유다.

 

함수를 호출하면 배열이 반환된다. 배열의 첫 번재 원소는 현재 상태이고, 두 번째 원소는 상태를 바꾸어 주는 함수이다. 이 함수를 세터함수라고 부른다. 그리고 배열 비구조화 할당을 통해 이름을 자유롭게 정해 줄수 있다. 

3.4.2.3 한 컴포넌트에서 useState 여러 번 사용하기

useState는 한 컴포넌트에서 여러 번 사용해도 상관없다.

3.5 state를 사용할 때 주의 사항

클래스형 컴포넌트든 함수 컴포넌트든 state를 사용할 때는 주의해야 할 사항이 있다. state 값을 바꾸어야 할 때는 setState 혹은 useState를 통해 전달받은 세터 함수를 사용해야 한다.

 

예를 들어 다음 코드는 잘못된 코드이다.

// 클래스형 컴포넌트에서
this.state.number = this.state.number + 1;
this.state.array = this.array.push(2);
this.state.object.value = 5;

// 함수형 컴포넌트에서
const [object, setObject] = useState({a: 1, b: 1});
object.b = 2;

배열이나 객체를 업데이트해야 할 때는 배열이나 객체 사본을 만들고 그 사본에 값을 업데이트한 후, 그 사본의 상태를 setState 혹은 세터 함수를 통해 업데이트 한다.

// 객체 다루기
const object = {a: 1, b: 2, c: 3};
const nextObject = {... object, b: 2}; // 사본을 만들어서 b 값만 덮어 쓰기

// 배열 다루기
const array = [
  { id: 1, value: true },
  { id: 2, value: true },
  { id: 3, value: false}
];

let nextArray = array.concat({id: 4}); //  새 항목 추가
nextArray.fillter(item => item.id !== 2); // id가 2인 항목 제거
nextArray.map(item => 1 ? {...item, value: false} : item)); // id가 1인 항목의 value를 false로 설정

객체에 대한 사본을 만들 때는 spread 연산자라 불리는 ...을 사용하여 처리하고, 배열에 대한 사본을 만들 때는 배열의 내장 함수들을 활용한다.

3.6 정리

props와 state는 둘 다 컴포넌트에서 사용하거나 렌더링할 데이터를 담고 있으므로 비슷해 보일 수 있지만, 그 역할은 매우 다르다. props는 부모 컴포넌트가 설정하고, state는 컴포넌트 자체적으로 지닌 값으로 컴포넌트 내부에서 값을 업데이트할 수 있다.

props를 사용한다고 해서 값이 무조건 고정적이지는 않다. 부모 컴포넌트의 state를 자식 컴포넌트의 props로 전달하고 , 자식 컴포넌트에서 특정 이벤트가 발생할 때 부모 컴포넌트의 메서드를 호출하면 props도 유동적으로 사용할 수 있다.

useState를 사용하는 것을 권장한다. 코드가 더 간결해질 뿐만 아니라, 리액트 개발 팀이 함수 컴포넌트와 Hooks를 사용하는 것이 주요 컴포넌트 개발 방식이 될 것이라고 공지했기 때문이다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함