실전 리엑트 프로그래밍 3장

3장. 중요하지만 헷갈리는 리엑트 개념 이해하기

UI 데이터가 변경되면 리엑트가 render 함수를 이용해 화면을 자동으로 갱신해 주며 이것이 리엑트의 가장 중요한 역할이다.

  • 리엑트를 사용하지 않고 직접 UI를 관리하는 기존의 프로그램 방식은, 화면을 어떻게 그릴지를 나타내는 반면

    → 명령형(imperative) 프로그래밍 이라고 부르고

    → 보통 dom을 직접 수정하면서 화면을 어떻게 그리는지 구체적으로 나타내기 때문에 dom 환경이 아닌 곳에서는 사용하기 어렵다.

  • 리엑트를 사용한 UI코드는 화면에 무엇을 그릴지 나타낸다

    → 선언형(declarative) 프로그래밍이라 부른다.

    → 무엇을 그리는지만 나타내고 있고, render함수가 선언형으로 작성되기 때문에 dom 환경 뿐 아니라 모바일 native의 UI도 나타낼 수 있다.

    → 즉 선언형은 명령형보다 추상화 단계가 높다고 할 수 있고, 비지니스 로직에 좀더 집중할 수 있는 장점이 있다.

State, Props..

State

  • 컴포넌트의 상탯값(state)을 이용하면 리엑트가 UI 데이터의 변경을 알 수 있다.

  • state의 값은 setState를 이용해 변경할 수 있는데, 비동기로 동작한다

    → react는 효율적으로 rendering하기 위해 여러개의 setState를 batch로 처리한다.

  • setState는 호출되기 직전의 state값을 매개변수로 받는다

this.setState(prevState => ({ someState: prevState.someState + 1 }));
  • setState가 비동기로 처리되니 처리된 시점을 알고 싶을 수 있는데, setState 의 두번째 매개 변수는 처리가 끝났을 때 호출되는 callback function이다.
onClick = () => {
this.setState({ someState: 'hahaha'}, () => console.log('state changed.'));
}
  • state는 기본적으로 불변값이 아니다. 하지만 state도 불변값으로 관리하는 것이 좋다. 코드의 복잡도가 낮아지는 장점이 있기 때문이다. 또 렌더링 성능도 좋아진다!

Props

  • 속성값(props)은 부모 컴포넌트가 전달해주는 데이터이고 대부분 UI데이터를 포함한다.
  • 기본적으로 불변(immutable)값이다.
  • 자식 컴포넌트는 별 조치가 없으면 부모 컴포넌트가 rendering 될 때 마다 같이 rendering된다.
  • 이를 방지하고 싶으면 React.memo 나 PureComponent를 이용할 수 있다.
const Title = props => <p>{props.title}</p>;
export default React.memo(Title);
// or
export default class Title extends React.PureComponent {
render() {
return <p>{props.title}</p>;
}
}

Instance..

class component는 리엑트 내부에서 instance로 존재하며,

각 instance는 자신만의 메모리 공간을 갖고 있기 때문에 각각의 상태값이 존재한다.

React Element & Virtual Dom

리엑트는 빠른 렌더링을 위해 메모리에 virtual dom을 올려두고 이전과 이후의 virtual dom을 비교해서 변경된 부분만 실제 dom에 반영하는 전략을 채택했다.

React Element

  • React가 UI를 표현하는 수단.

  • 불변객체이므로 속성값을 변경할 수 없다.

  • 리엑트에서 데이터 변경에 의한 화면 업데이트는 render phase(또는 reconciliation phase)와 commit phase를 거친다.

    → render phase는 돔에 반영할 변경 사항을 파악하는 단계, virtual dom을 이용함.

    → render phase는 ReactDom.render() 와 setState() 에 의해 시작된다.

    → commit phase는 변경 사항을 실제 돔에 반영하는 단계

  • 화면을 표현하기 위해 다수의 React element 를 tree 구조로 구성하게 된다.

    → react element가 실제 dom으로 만들어지기 위해서는 모든 element의 type 속성값이 문자열이어야 한다. 그래야 HTML 태그로 변환할 수 있기 때문이다. 그러기 위해서는 모든 컴포넌트의 render함수가 호출되어야 한다.

    const Title = ({ title }) => <p style={{ color: 'blue' }}>{title}</p>;
    export default React.memo(Title);
    // 의 JSX가 createElement 로 변경되어
    const elementTree = {
    type: 'div',
    props: {
    children: [
    {
    type: 'p',
    props: {
    stype: { color: 'blue' }
    }
    }
    .....
    ]
    }
    }
    // 이렇게 모든 type이 문자열로 변경이 되어야 render phase에서 이전 element와의 값 비교를
    // 할 수 있게 되고, 변경된 영역 (예를들면 'p')만 콕 집어 거기만 업데이트 쳐준다.

Lifecycle method

모든 컴포넌트는 초기화 단계, 업데이트 단계, 소멸 단계의 세 단계를 거친다.

(어디서 책이랑 똑같이 생긴 그림을 찾았네.. 라고 생각했는데 책에 써있는 주소랑 똑같은걸 찾았다 ㅋㅋ)

https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

react-programming/react-lifecycle.png

Mounting

  • 최초 컴포넌트 객체가 생성될 때 한 번 수행됨.
  • constructor() → static getDerivedStateFromProps() → render() → componentDidMount() 의 순서로 호출됨.

Updating

  • Mount와 UnMount단계 사이에서 반복해서 수행됨.
  • Component의 state나 props가 변경되면 Updating 단계가 수행된다.
  • static getDerivedStateFromProps() → shouldComponentUpdate() → render() → getSnapshotBeforeUpdate() → componentDidUpdate() 의 순서로 호출됨.

렌더링 시 예외가 발생하면

static getDerivedStateFromError(), componentDidCatch() 가 호출됨.

Constructor

  • super()를 호출해야 React.Component class의 constructor()가 호출됨. 안쓰면 호출이 안되지만 개발 모드에서 예외를 발생시킴.
  • state를 직접 할당할 수 있는 유일한 메서드
constructor(props) {
super(props);
this.state = { state1: 'hjahah' };
}
  • js의 표준이 될 것이 확실한 class field를 이용하면 constructor 없이 쓸 수 있음. (CRA에서도 지원함)
class Foo extends React.Component {
state {
state1: 'FOOOOO',
}
}
  • setState() 는 component mount된 이후에만 유효하므로 constructor 에서 setState를 호출해도 씹힘.

static getDerivedStateFromProps(props, state)

  • 문자 그대로 props를 이용해 새로운 state를 만들 때 사용됨.
  • render가 호출되기 직전에 호출됨.
  • static method이므로 함수 내부에서 this 접근할 수 없다.
  • 시간에 따라 변하는 props로부터 state를 계산하기 위해 추가되었고 보통 animation과 관련된 props로부터 state를 계산할 때 유용하다.
  • 매개변수에 props만 있고 prevProps가 없으므로, state에 prevProps를 저장해야 한다.
state = {
prevSomething: this.props.something
}
static getDerivedStateFromProps(props, state) {
if(props.something !== state.prevSomething) {
// do something.. . .
}
}
  • React team이 method에 prevProps를 넣지 않는 이유는 최초 호출시 이전 props값이 없기 때문에 null check를 항상 해줘야 하고, 앞으로 모든 lifecycle method에 prev-props값을 삭제할 예정임. —> 메모리 절약면에서도 좋고..
  • 이 method를 제대로 사용하지 못하는 경우 : props변화에 따라 API호출하려는 경우, props입력으로 하는 memoization을 state로 관리하려는 경우, props가 변경될 때 state를 초기화 하려는 경우.. 등

J3
백엔드, 프론트엔드 가리지 않고 모두 관심많은 초절정미녀 개발자입니다~!!