✔ Virtual DOM을 이용한다.
✔ 이미 많은 곳에서 사용하고 있다.
✔ 다양한 라이브러리가 존재한다.
✔ 라우터, 상태관리, 스타일링 등 기능들이 내장되어 있지 않고 써드파티 라이브러리가 존재한다.
(자유도가 높아 원하는 방식을 선택해 사용할 수 있다.)
✔ Node, yarn, Webpack, Babel 등의 도구를 설치하여 프로젝트 설정하기
✔ 페이스북에서 제공하는 create-react-app 을 통해 설치하면 위 작업을 간단하게 준비할 수 있다.
✔ Webpack : 여러가지의 파일로 저장된 컴포넌트들을 한 개로 결합해주는 역할
✔ Babel : JSX를 비롯한 새로운 자바스크립트 문법들을 사용할 수 있게 해주는 역할
- 노드 공식 홈페이지를 통해 Node.js 설치하기
- yarn 설치 페이지를 통해 yarn 설치하기
- 코드 에디터 준비하기 (VSCode, Atom, WebStorm, Sublime ..)
- 터미널창에서 create-react-app 생성 'yarn create react-app [폴더명]'
- 'yarn start'
✔ JSX 내부에서 조건부 렌더링을 할 때는 보통 삼항연산자 혹은 AND 연산자를 사용한다.
{ 1 + 1 === 2 && (<div>맞아요!</div>) }
✔ if문을 사용하려면 즉시 실행 함수 표현(IIFE)을 사용해야 한다.
{ (() => { if (value === 1) return (<div>하나</div>); if (value === 2) return (<div>둘</div>); if (value === 3) return (<div>셋</div>); })() }
✔ 부모컴포넌트에서 자식 컴포넌트로 값을 전해줄 때 props를 이용
//App.js 부모컴포넌트 return( <Myname name="bella">자식요소</> )
//Myname.js 자식컴포넌트 return( <> <h1>제 이름은 {this.props.name} 입니다.</h1> <h2>이것은 {this.props.children} 값입니다.</h2> </> )
//Result ➡ 제 이름은 bella 입니다. ➡ 이것은 자식요소 값입니다.
✔ PropTypes를 이용해 타입 설정
- import PropTypes from "prop-types"; 상단에 추가해주기//Myname.js class Myname extends Component { ... } Myname.propTypes = { name: PropTypes.string.isRequired }
✔ defaultProps를 이용해 기본값 설정
//Myname.js class Myname extends Component { ... } Myname.defaultProps = { name: "unkown", children: "자식요소" }
✔ 동적인 데이터를 다룰 때 사용
📍 스테이트 정의
//class fields 문법 class Counter extends Component { state = { number: 0, } ... }
//constructor를 작성하게 되면 기존 클래스 생성자를 덮어쓰게 됨 // -> super(props) 를 호출해주어 리액트 컴포넌트가 가지고 있던 생성자를 미리 실행해줘야 함 class Counter extends Component { constructor(props) { super(props); this.state = { number: 0 } } ... }
❓ class fields 와 constructor 를 동시에 사용한다면?
class fields 가 먼저 실행되고, 그 다음에 constructor 에서 설정된 것이 실행
📍 메소드 작성
handleIncrease = () => { this.setState({ number: this.state.number + 1 }); } handleDecrease = () => { this.setState({ number: this.state.number - 1 }); }
화살표 함수를 이용하지 않을경우 클릭이벤트 발생 시, this가 undefined으로 나타나 제대로 동작하지 않게 된다.
이를 방지하기 위해선 constructor에 아래 코드를 추가해줘야 한다.constructor(props) { super(props); this.handleIncrease = this.handleIncrease.bind(this); this.handleDecrease = this.handleDecrease.bind(this); }
📍 setState
- state 값을 바꾸기 위해 사용하는 함수, 객체로 전달되는 값만 업데이트를 해준다.
- 리액트에서는 setState 함수가 호출될때마다 컴포넌트가 리렌더딩 되도록 설계되어 있다.
- 객체의 깊숙한곳까지 확인하지는 못한다state = { number: 0, foo: { bar: 0, foobar: 1 } } this.setState({ foo: { foobar: 2 } })
//Result : 기존 foo 객체가 변경됨 { number: 0, foo: { foobar: 2 } }
- 기존값을 유지하면서 변경하려면 전개연산자(...)를 이용
this.setState({ number: 0, foo: { ...this.state.foo, foobar: 2 } });
- 객체대신 함수 전달하기 비구조화 할당
handleIncrease = () => { const { number } = this.state; this.setState({ number: number + 1 }); } handleDecrease = () => { this.setState( ({ number }) => ({ number: number - 1 }) ); }
✔ 컴포넌트가 브라우저에서 나타날때, 사라질때, 그리고 업데이트 될 때, 호출되는 API
📍 컴포넌트 초기생성
constructor(props) { super(props); // 컴포넌트가 새로 만들어질 때마다 호출되는 컴포넌트 생성자 함수 }
componentWillMount() { // 컴포넌트가 브라우저에 나타나기 직전에 호출되는 API // 기존에 해당 API가 하던 일을 constructor와 componentDidMount에서 처리가 가능 // 해당 API는 더이상 필요하지 않게 됨 // v16.3 이후로는 'UNSAFE_componentWillMount()'라는 이름으로 사용 }
componentDidMount() { // 컴포넌트가 브라우저에 나타났을 때 호출되는 API // DOM을 사용해야 하는 외부 라이브러리 연동 : D3, masonry, etc // 컴포넌트에서 필요한 데이터 요청: Ajax, GraphQL, etc // DOM 에 관련된 작업: 스크롤 설정, 크기 읽어오기 등 }
📍 컴포넌트 업데이트
componentWillReceiveProps(nextProps) { // 컴포넌트가 새로운 props를 받게됐을 때 호출되는 API // this.props는 아직 바뀌지 않은 상태, 새로 받게될 props는 nextProps로 조회 // 주로 state가 props에 따라 변해야하는 로직을 작성 // v16.3 이후로는 'UNSAFE_componentWillReceiveProps()'라는 이름으로 사용 // 상황에 따라 getDerivedStateFromProps로 대체가능 }
static getDerivedStateFromProps(nextProps, prevState) { // v16.3 이후에 만들어진 라이프사이클 API // props 로 받아온 값을 state 로 동기화 하는 작업을 해줘야 하는 경우에 사용 // setState 하는 것이 아닌, 특정 props가 바뀔 때 설정하고 싶은 state값을 리턴하는 형태로 사용 if (nextProps.value !== prevState.value) { return { value: nextProps.value }; } }
shouldComponentUpdate(nextProps, nextState) { // 기본적으로 true를 반환, return false 하면 업데이트를 안함 // return this.props.checked !== nextProps.checked }
componentWillUpdate(nextProps, nextState) { // shouldComponentUpdate 에서 true 를 반환했을때만 호출 // 주로 애니메이션 효과를 초기화하거나, 이벤트 리스너를 없애는 작업을 진행 // 해당 함수가 호출된 이후, render()가 호출됨 // v16.3 이후로 사용되지 않음, getSnapshotBeforeUpdate로 대체 가능 }
getSnapshotBeforeUpdate(prevProps, prevState) { // DOM 변화가 일어나기 직전의 DOM 상태를 가져옴 // 여기서 리턴하는 값은 componentDidUpdate 에서 3번째 파라미터로 받아올 수 있음 // 해당 API가 발생하는 시점 : // render(), getSnapshotBeforeUpdate(), 실제 DOM에 변화 발생, componentDidUpdate }
componentDidUpdate(prevProps, prevState, snapshot) { // render()를 호출하고 난 다음에 발생 // this.props 와 this.state 가 바뀌어있는 상태 // 파라미터를 통해 이전 값, prevProps와 prevState 조회 가능 // getSnapshotBeforeUpdate에서 반환한 snapshot값을 세번째 파라미터로 받아옴 }
📍 컴포넌트 제거
componentWillUnmount() { // 이벤트, setTimeout, 외부 라이브러리 인스턴스 제거 }
📍 컴포넌트 에러 발생
componentDidCatch(error, info) { this.setState({ error: true }); } render() { if (this.state.error) return (<h1>에러발생!</h1>); }