실전 리엑트 프로그래밍 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);// orexport 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/
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를 초기화 하려는 경우.. 등