티스토리 뷰

프로그래밍/React

[문법]props와 state

개발자 카니 2019. 1. 31. 11:03

 React Component에서 다루는 데이터는 props와 state가 있습니다.


props


 props는 자식 Component가 생성될 때 부모 Component에서 받아온 데이터로 변경이 불가능합니다. 자식 Component에서 this라는 키워드를 통해 부모 Component로부터 받은 props를 사용할 수 있습니다.


 src 폴더에 Card.js라는 파일을 만들고 다음과 같은 내용을 입력합니다.


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
// Card.js
import React, { Component } from "react";
 
class Card extends Component {
  render() {
    return (
      <React.Fragment>
        <h3>
          이름 : <b>{this.props.name}</b>
        </h3>
        <h3>
          소속 : <b>{this.props.department}</b>
        </h3>
        <h3>
          연락처 : <b>{this.props.phone}</b>
        </h3>
        <h3>
          이메일 : <b>{this.props.email}</b>
        </h3>
      </React.Fragment>
    );
  }
}
 
export default Card;
cs


 다음은 App.js를 수정합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// App.js
import React, { Component } from "react";
import Card from "./Card";
 
class App extends Component {
  render() {
    return (
      <Card
        name="Kani"
        department="Dev"
        phone="010-1234-5678"
        email="kani@kani.com"
      />
    );
  }
}
 
export default App;
cs


 다음과 같은 구조에서는 Card가 App의 자식 Component가 되는겁니다. 이 때, 부모 Component(App.js)에서 지정한 name, department, phone, email이 자식 Component(Card.js)로 전달되어 Card에서 this.props.name, this.props.department, this.props.phone, this.props.email이 다음과 같이 사용됩니다. 결과는?



defaultProps


 부모 Component가 여러 개의 공통된 자식 Component를 사용할 경우가 있습니다. 하지만 자식 Component에서 공통적으로 사용되는 props와 값이 다양한 props가 있을 수 있습니다. 이럴 경우 자식 Component에서는 defaultProps를 이용하여 props의 기본값을 설정해 줄 수 있습니다. 이 값은 부모 Component에서 지정해 줄 경우 지정한 값으로 지정하지 않을 경우 기본값으로 props가 적용됩니다.

 

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
31
32
33
34
35
36
// Card.js
import React, { Component } from "react";
 
class Card extends Component {
  // 기본 props 지정
  static defaultProps = {
    department: "Dev"
  };
  render() {
    return (
      <React.Fragment>
        <h3>
          이름 : <b>{this.props.name}</b>
        </h3>
        <h3>
          소속 : <b>{this.props.department}</b>
        </h3>
        <h3>
          연락처 : <b>{this.props.phone}</b>
        </h3>
        <h3>
          이메일 : <b>{this.props.email}</b>
        </h3>
      </React.Fragment>
    );
  }
}
 
// 위의 static 부분을 지우고 이렇게도 지정 가능(주석 제거후 사용)
/*
Card.defaultProps = {
    department: "Dev"
};
*/
 
export default Card;
cs


 defaultProps를 통해 복수의 props의 기본값을 설정할 수 있지만 department의 기본값만 설정해보겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// App.js
import React, { Component } from "react";
import Card from "./Card";
 
class App extends Component {
  render() {
    return (
      <React.Fragment>
        {/* department 생략 */}
        <Card name="Kani" phone="010-1234-5678" email="kani@kani.com" />
        <hr />
        {/* department 포함 */}
        <Card
          name="Bob"
          department="Marketing"
          phone="010-5678-1234"
          email="bob@bob.com"
        />
      </React.Fragment>
    );
  }
}
 
export default App;
cs


 App에서 2개의 Card를 만듭니다. 하나는 department가 생략하고 다른 하나는 department를 포함했습니다. 결과는?



Functional Component(함수형 컴포넌트)


 - 함수형 컴포넌트는 클래스형 컴포넌트외에 다른 방법으로 컴포넌트를 생성하는 방법입니다. 단순히 props를 받아와 보여주기만 하는 컴포넌트의 경우 더 간단한 문법으로 사용할 수 있습니다. 

 - 함수형 컴포넌트는 컴포넌트가 함수이기 때문에 클래스의 특성에서 지닐수 있는 static이나 this 키워드를 사용할 수 없습니다. 그리고 state나 Life Cycle과 관련된 함수를 사용할 수 없습니다. 

 - 초기에 마운트될 때 미세하게 빠르고 메모리를 덜 사용합니다. 하지만 컴포넌트가 엄청 많지 않으면 성능상 차이는 거의 없습니다.


 기존에 작성한 Card.js를 수정해봅시다.


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
// Card.js
import React from "react";
 
const Card = ({ name, department, phone, email }) => {
  return (
    <React.Fragment>
      <h3>
        이름 : <b>{name}</b>
      </h3>
      <h3>
        소속 : <b>{department}</b>
      </h3>
      <h3>
        연락처 : <b>{phone}</b>
      </h3>
      <h3>
        이메일 : <b>{email}</b>
      </h3>
    </React.Fragment>
  );
};
 
// 기본 props 지정(함수형 컴포넌트 사용시 static을 사용불가)
Card.defaultProps = {
  department: "Dev"
};
 
export default Card;
cs


 다음과 같이 함수의 매개변수로 props의 인자들을 직접 받습니다. 결과는 동일하기 때문에 생략합니다.


state


 state는 Component 내부에서 생성된 데이터로 변경이 가능합니다. Component 내부에 state를 class field나 constructor(props) 내부에 정의해서 사용합니다. 이 때, 혹시 두가지를 같이 사용하게 되면 class field가 먼저 정의됩니다.


Method Binding


 이벤트를 처리할 경우 임의의 메소드를 만드는 경우가 많습니다. 이 때, 해당 메소드에서 state를 변경하기 위해서 this 키워드를 사용하려면 메소드에 this를 바인딩시켜주어야 합니다. constructor(props) 내부에 "this.메소드명 = this.메소드명.bind(this)"를 사용하여 메소드에 this를 바인딩시킵니다. 이 작업을 하지 않을 경우 이벤트가 발생했을 경우에 해당 메소드가 이벤트로 전달되는 과정에서 this와의 연결이 끊어져서 undefined가 됩니다. 하지만 this를 메소드를 화살표 함수 표현(Arrow Function Expression)으로 만들 경우 this가 풀리는 것에 대해 걱정할 필요가 없으므로 method binding을 명시적으로 하지 않아도 됩니다.


setState


 - 선언된 state 값을 변경하기 위해서는 무조건 this.setState를 사용해야 합니다. React 내부에서 setState를 호출하게 되면 자동으로 Component를 리렌더링하게 설계되어있습니다. 

 - 객체로 전달되는 해당 값만 state에서 변경됩니다. 전달되지 않은 값은 기존값으로 유지됩니다. state 내부에 객체가 존재하고 그 객체의 특정 인자만 변경하고 싶다면 "객체1: { ..., this.state.객체1, 변경요소: 변경요소 값 }"의 다음과 같은 구조로 작성해야 합니다. 그렇지 않으면 객체 자체를 새로 덮어 써버립니다. 전개 연산자(Spread Operator, ...)을 사용하면 기존 내용을 해당 객체에 풀어줍니다. 이 부분은 객체의 depth가 깊어지면 상당히 복잡해지므로 immutable.js나 immer.js를 이용해서 간단화 작업을 하는게 좋습니다.

 - 기존의 값을 토대로 새로운 값으로 변경하고 싶을 땐 "값1: this.state.값1" 이런 식으로 처리를 합니다. this.state를 사용하고 싶지않다면 setState를 화살표 함수로 만들어서 매개변수로 state나 해당 값을 넣어주면 됩니다. 이 때, 해당 값을 이용할 땐 비구조화 할당(Destructuring Assignment)이라는 문법을 사용합니다.

 - render() 함수 내부의 HTML 코드에서 이벤트를 설정할 땐, camelCase로 이벤트명을 적어야 합니다. 또한 이벤트 속성의 값으로는 "함수()" 형태가 아닌 "함수명"으로 적어야 합니다. 그렇지 않을 경우 함수 호출이 무한 루프에 빠질수 있습니다. (렌더링 -> 함수 호출 -> setState -> 리렌더링 -> 함수 호출 -> ...)


 src 폴더에 StopWatch.js라는 파일을 만들고 다음과 같은 내용을 입력합니다.


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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// StopWatch.js
import React, { Component } from "react";
 
class StopWatch extends Component {
  // class fields
  state = {
    sec: 0,
    buttonFlag: true,
    intervalFunction: null
  };
 
  // stop watch 로직
  timer = () => {
    // 비구조화 할당 사용( { sec } 부분 )
    this.setState(({ sec }) => ({
      sec: sec + 1
    }));
  };
 
  // start 버튼 클릭
  start = () => {
    this.setState({
      sec: 0,
      buttonFlag: false,
      intervalFunction: setInterval(this.timer, 1000)
    });
  };
 
  // stop 버튼 클릭
  stop = () => {
    clearInterval(this.state.intervalFunction);
 
    this.setState({
      buttonFlag: true
    });
  };
 
  render() {
    return (
      <React.Fragment>
        <h1>Stop Watch</h1>
        <div>
          <b>{this.state.sec}</b>
          <span></span>
          {this.state.buttonFlag ? (
            <button onClick={this.start}>start</button>
          ) : (
            <button onClick={this.stop}>stop</button>
          )}
        </div>
      </React.Fragment>
    );
  }
}
 
export default StopWatch;
cs


 다음은 App.js를 수정합니다.


1
2
3
4
5
6
7
8
9
10
11
// App.js
import React, { Component } from "react";
import StopWatch from "./StopWatch";
 
class App extends Component {
  render() {
    return <StopWatch />;
  }
}
 
export default App;
cs


 javascript의 내장 함수인 setInterval과 clearInterval을 이용해서 구현한 스톱 워치입니다. 결과는?


References


[Velopert 블로그 props와 state] https://velopert.com/3629

'프로그래밍 > React' 카테고리의 다른 글

[예제]간단한 가계부1 - input 상태 관리  (0) 2019.02.06
[문법]Life Cycle API  (0) 2019.02.04
[문법]JSX  (0) 2019.01.29
React 프로젝트 시작하기  (0) 2019.01.29
React란?  (0) 2019.01.29
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
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 31
글 보관함