v16.8 부터 리액트는 함수형과 hooks라는 큰 변화를 맞이했다.
그래도 공식 문서의 이번 장은 주로 클래스 컴포넌트로 설명돼 있었는데,
새로운 공식문서(베타)는 useState()라는 Hook으로 설명한다. 함수형 + hook으로 본격적으로 바꾸고, 클래스형은 슬슬 버려지는 듯 하다.
본 페이지에서는 this.state를 설명하긴 하되, useState() 훅 위주로 정리하겠다.
클래스 컴포넌트는 state를 컴포넌트 클래스의 하나의 메소드로 본다. 정의된 클래스에서 private하게 완전히 제어된다고 한다. (그래서 전역상태관리 라이브러리를 사용하기도 함)
component의 생명주기를 설명하기 위해 적어봤다. 실제로 이렇게 state를 쓰는 개발자는 없을것이다.
작동을 안한다. setState() 함수를 사용하지 않으면 다시 렌더링을 안함!
리액트의 정신 = 빠른 반응, 프런트에서 성능 향상. 누르면 앱처럼 제깍제깍 작동해야 함.
그리고 render 되기 전에 실행되어야 하기 때문이라고 한다.
그래서 비동기로 만들어놨음. 장점이 많지만, 이것 때문에 자칫하면 고생할 수 있다.
클래스 컴포넌트의 state는 하나의 큰 객체.
여러개의 state를 선언했는데 하나만 setState() 로 업데이트 했다면, 하나만 바뀐 state 객체가 덮어씌워진다. (마치 git의 merge와 비슷한 과정). 바뀌지 않은 state 값을 그대로 두는 것은 병합이 shallow 하기 때문이라고 한다.
잘못된 예시 1 setState() 후 state를 바로 호출하는 경우
setState()가 비동기라서 겪는 가장 대표적 사례일 것이다.// 잘못된 코드 예시 this.setState({ count: this.state.count + 1 }); console.log(this.state.count); // <- setState() 후 바로 호출.
비동기라서 setState()는 잠시 기다리고 (이벤트 루프 행), console.log() 가 먼저 실행된다.
잘못된 예시 2 setState() 연속기
<button onClick={()=>{ setCount(count+1); if ( count < 3 ) { setAge(age+1); } }}>누르면한살먹기</button>
setCount()는 비동기 이기 때문에 if 문이 실행되는 시점이랑 개발자가 원하는 count값의 변동 시점이 안맞는다.
이를 어찌 해결한다? updater 함수를 인자로 집어넣는다 !
올바른 예시
setState((prev) => {prev + 1})
- 리액트는 리렌더링까지 state를 업데이트하지 않음.
- 리렌더링 시 여러개의 setState()를 배치로 실행하게 된다.
- 그래서 prev 상태를 명시해주는 updater 함수를 사용해야 업데이트 시점이 더 명확해짐.
state는 private 하다. 다른 컴포넌트의 상태를 알 수 없다는 뜻.
그래서 다른 컴포넌트에서 state에 접근하고 싶다면, props로 전달해야 한다.
props는 부모에서 자식으로 전달된다고 했다. 즉 state는 부모에서 자식으로, props의 형태로만 가능한 것.
게다가 props에 들어있는 값을 꺼내 쓰는 것이기 때문에, 자식 컴포넌트는 그 값이 state인지, let인지, const인지 관심이 없다!