go to article

javascript/react.js

[React-Native] 함수형 컴포넌트 vs 클래스 컴포넌트

** 참고

리액트가 왜 함수형 컴포넌트를 선택했으며, 리액트 훅이 왜 나온것인지
https://ko.reactjs.org/docs/hooks-intro.html#motivation

 

함수형 컴포넌트

import React, {useState} from 'react';
import Styled from 'styled-components/native';
import Button from '~/Components/Button';

const Container = Styled.SafeAreaView`
    flex: 1;
`;

const TitleContainer = Styled.View`
    flex: 1;
    justify-content: center;
    align-items: center;
`;

const TitleLabel = Styled.Text`
    font-size: 24px;
`;

const CounterContainer = Styled.View`
    flex: 2;
    justify-content: center;
    align-items: center;
`;

const CountLabel = Styled.Text`
    font-size: 24px;
    font-weight: bold;
`;

const ButtonContainer = Styled.View`
    flex: 1;
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: space-around;
`;

interface Props {
  title?: string;
  initValue: number;
}

const index = ({title, initValue}: Props) => {
  const [count, setCount] = useState<number>(initValue);

  return (
    <Container>
      {title && (
        <TitleContainer>
          <TitleLabel>{title}</TitleLabel>
        </TitleContainer>
      )}
      <CounterContainer>
        <CountLabel>{initValue + count}</CountLabel>
      </CounterContainer>
      <ButtonContainer>
        <Button iconName="plus" onPress={() => setCount(count + 1)} />
        <Button
          iconName="minus"
          onPress={() => setCount(count > 0 ? count - 1 : count)}
        />
      </ButtonContainer>
    </Container>
  );
};

export default index;

 

클래스 컴포넌트

...

interface Props {
  title?: string;
  initValue: number;
}

/**
 * 클래스 컴포넌트에서 State를 사용하는 경우, 함수형 컴포넌트와는 다르게
 * State의 타입을 미리 정의하고 컴포넌트 선언 시 해당 타입을 지정한다.
 */
interface State {
  count: number;
  error: Boolean;
}

class Counter extends React.Component<Props, State> {
  constructor(props: Props) {
    /**
     * 생성자 함수에서는 항상 super(props);를 사용하여
     * 부모 컴포넌트(React.Component)의 생성자 함수를 호출해야 한다는 점이다.
     */
    super(props);
    console.log('constructor');

    /**
     * 함수형 컴포넌트에서는 useState로 State를 생성할 때, 초기값을 지정했으나
     * 클래스 컴포넌트에서는 생성자에서 State의 초기값을 지정한다.
     */
    this.state = {
      count: props.initValue,
      error: false,
    };
  }

  render() {
    /**
     * 클래스 컴포넌트에서는 함수형 컴포넌트와는 다르게
     * Props와 State에 접근하기 위해서 this를 함께 사용한다.
     */
    const {title} = this.props;
    const {count, error} = this.state;

    return (
      <Container>
        {!error && (
          <>
            {title && (
              <TitleContainer>
                <TitleLabel>{title}</TitleLabel>
              </TitleContainer>
            )}
            <CounterContainer>
              <CountLabel>{count}</CountLabel>
            </CounterContainer>
            <ButtonContainer>
              <Button
                iconName="plus"
                /**
                 * State는 불변값이므로 변경하고자 할 때는
                 * this.setState 함수를 사용하여 State 값을 변경한다.
                 */
                onPress={() => this.setState({count: count + 1})}
              />
              <Button
                iconName="minus"
                onPress={() =>
                  this.setState({count: count > 0 ? count - 1 : count})
                }
              />
            </ButtonContainer>
          </>
        )}
      </Container>
    );
  }

  /**
   * 클래스 컴포넌트는 함수형 컴포넌트와 다르게 라이프 사이클 함수들을 가지고 있다.
   */
  static getDerivedStateFromProps(nextProps: Props, prevState: State) {
    console.log('getDerivedStateFromProps');
    return null;
  }

  componentDidMount() {
    console.log('componentDidMount');
  }

  getSnapshotBeforeUpdate(prevProps: Props, prevState: State) {
    console.log('getSnapshotBeforeUpdate');
    return {
      testData: true,
    };
  }

  componentDidUpdate(prevProps: Props, prevState: State, snapshot: ISnapshot) {
    console.log('componentDidUpdate');
  }

  shouldComponentUpdate(nextProps: Props, nextState: State) {
    console.log('shouldComponentUpdate');
    return true;
  }

  componentWillUnmount() {
    console.log('componentWillUnmount');
  }

  componentDidCatch(error: Error, info: React.ErrorInfo) {
    this.state({
      error: true,
    });
  }
}

export default Counter;