어서와, 개발은 처음이지?

4.React Native State와 Props - 1부(State) 본문

React/React Native

4.React Native State와 Props - 1부(State)

오지고지리고알파고포켓몬고 2018. 7. 23. 15:51


이 글은 4.React Native State와 Props - 2부(Props)와 이어집니다



지난 글에서 JSX와 함께 State와 Props가 언급되었습니다.

리액트는 State와 Props라는 객체를 사용하여 화면에 표시되는 가변적인 값들을 관리합니다.

State와 Props는 몇가지 특징을 가지고 있습니다.



State와 Props의 특징


1. Props 혹은 State에 변경이 감지될 때마다 render()가 수행된다. (이는 리액트의 최적화와도 관련있습니다.)

2. State에는 현재 컴포넌트의 화면을 그리는 것과 관련된 대다수의 값들을 담는다.

3. 데이터의 흐름은 상위 컴포넌트에서 하위 컴포넌트로 단방향이다.

4. Props에는 상위 컴포넌트에서 전달받은 값이 담겨있으며 변경 불가능하다.

5. Props 혹은 State는 비동기적(asynchronously)으로 업데이트 될 수 있다.



State


2번이 어떤 상황이냐면, 첫 화면에 '환영합니다' 처럼 어떤 상황에도 변하지 않는 내용은 State에 담을 필요가 없습니다.

하지만 '환영합니다 ooo님'이라고 표현해야 한다면 State에 각 사용자별 이름이 담겨있어야 한다는 뜻이죠.

이를 대략적으로 표현하면 '환영합니다 {this.state.name}님' 이라고 쓸 수 있습니다.


좀 더 예를들자면, 인스타그램을 켰을 때 서버를 통해 받아온 글 목록이 State에 저장되고, State에 저장된 값을 render()해서 화면에 그리게 된다는 뜻 입니다.



처음에는 빈 화면이었지만, 글 목록이 State에 저장되면서 특징 1번에 의해 render()가 새로 수행되고, 글들이 그려지게 되는 것이죠.

간단한 예제로 State를 실습해보겠습니다.

import React, {Component} from 'react';
import {StyleSheet, Button, View} from 'react-native';

type Props = {};
export default class App extends Component<Props> {
  constructor(props){
    super(props);
    this.state={count:0};
  }

  _updateCount(){
    this.setState({
      count:this.state.count+1
    });
    // this.setState((prevState, props) => {
    //   return {count:prevState.count+1}
    // });
  }

  render() {
    return (
      <View style={styles.container}>
        <Button
          color="green"
          title={this.state.count.toString()}
          onPress={this._updateCount.bind(this)} />
      </View>
    );
  }
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
  },
});



위 코드를 App.js에 붙여넣고 실행합니다.

버튼을 누르면 숫자가 증가하는 것을 볼 수 있죠?


우선 코드를 훑어보겠습니다.

constructor(props){
    super(props);
    this.state={count:0};

    /*
    this.state={count:0};
    super(props);

    super()가 뒤로오면 안됩니다.
    super()로 React의 Component를 먼저 생성(호출)해줘야합니다.
    */
  }

constructor란 '생성자'라고 하며 클래스가 생성될 때 제일 먼저 실행되는 함수입니다.

이곳에서 this.state={ } 꼴로 State가 사용할 변수를 초기화 합니다.


super(props)는 상속받은 컴포넌트(extends Component)의 생성자를 호출하는 내용으로, 현재는 굳이 명시하지 않아도 됩니다.

하지만 직관적으로 작성하는 것을 선호하기에 작성해줍니다.

단, constructor()에서는 super()를 가장 먼저 실행하는것이 원칙입니다.

_updateCount(){
    this.setState({
      count:this.state.count+1
    });
    /*
    this.setState((prevState, props) => {
      return {count:prevState.count+1}
    });
    */
  }

State는 this.setState() 함수를 사용하여 업데이트할 수 있습니다.


setState()의 인자는 객체와 함수 모두 가능하고, 주석처리해둔 부분이 함수형으로 사용하는 방법입니다.

함수형으로 사용하는 것이 권장되는 방법이지만 그 차이를 느끼기 위해서 일단 객체형으로 사용하도록 합니다.

이 부분은 나중에 다시 설명하겠습니다.


기존 state.count에 1을 더한 값을 state에 업데이트 하는 것으로 _updateCount() 함수를 정의해줬습니다.

이 함수가 실행되서 state가 변화하면 render()가 새로 일어나게 됩니다.

render() {
    return (
      <View style={styles.container}>
        <Button
          color="green"
          title={this.state.count.toString()}
          onPress={this._updateCount.bind(this)} />
      </View>
    );
  }

Button 컴포넌트에 써있는 글자는 title이라는 속성으로 전달합니다. (태그를 닫지 않으면 에러가 나지만 <Button />처럼 닫는 태그를 한번에 쓸 수 있습니다.)

우리는 이 title에 state.count라는 값을 전달하여 몇번 눌렸는지 확인하도록 합니다.

또한 버튼을 눌렀을때 실행되는 함수는 onPress라는 속성으로 전달합니다.


bind(this)는 함수의 this를 현재 객체(클래스) 바운더리의 this로 지정하겠다는 뜻 입니다.

_updateCount()는 현 클래스의 this.state를 참조하기 때문에 bind(this)를 사용하여 꼭 지정해줍니다.

그렇지 않는다면 Button 컴포넌트에 있는 state를 참조하게 됩니다.


말이 어려울 수 있는데, 함수 안에 this.state나 this.props가 사용되고 있다면 bind를 해줘야한다고 기억합시다.


이 모든것을 도식화하면 아래 이미지와 같습니다.




판사님 State대신 그냥 변수는 안 되나요?



네 안됩니다.(근데 가능은 합니다)

import React, {Component} from 'react';
import {StyleSheet, Button, View} from 'react-native';

type Props = {};
var test = "hi"
export default class App extends Component<Props> {
  constructor(props){
    super(props);
  }

  _updateCount(){
    test = "bye";
  }

  render() {
    return (
      <View style={styles.container}>
        <Button
          color="green"
          title={test}
          onPress={this._updateCount.bind(this)} />
      </View>
    );
  }
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
  },
});

코드를 이렇게 바꿔봅니다.


test라는 변수에 "hi"라는 값을 넣고 Button 컴포넌트의 title에 넣어줬습니다.

그리고 버튼을 눌렀을 때 test의 내용을 "bye"로 바꿔주도록 합니다.



하지만 버튼을 아무리 눌러도 글자가 바뀌지 않네요? 왜 그럴까요?

위에서 언급한 특징 1번 때문입니다. 버튼을 누르면 test에는 bye라는 값이 들어가게 됩니다. 하지만 render()가 새로 수행되지 않기 때문이죠


약간의 트릭을 써서 증명해봅니다.

import React, {Component} from 'react';
import {StyleSheet, Button, View} from 'react-native';

type Props = {};
let test = "hi"
export default class App extends Component<Props> {
  constructor(props){
    super(props);
    this.state={color:"green"}
  }

  _updateCount(){
    test = "bye";
    this.setState({color:"red"});
  }

  render() {
    return (
      <View style={styles.container}>
        <Button
          color={this.state.color}
          title={test}
          onPress={this._updateCount.bind(this)} />
      </View>
    );
  }
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
  },
});

test는 그대로지만 State에 color를 추가해서 re render를 유발했습니다.



만약 실제 앱에서 화면에 뿌려줄 변수와 State를 별도로 관리하는 것은 지옥의 유지보수를 선사해주게 될 것 입니다.

(아 진짜 지옥같네)


이로써 State를 마치겠습니다.

많은걸 읽으셨으니 Props에 들어가기 앞서 한 10분 쉬었다 오세요.


아직 안끝났어..?




이 글은 4.React Native State와 Props - 2부(Props)와 이어집니다

2 Comments
댓글쓰기 폼