리액트 2주차 정리
Component Lifecycle
리액트 컴포넌트는 탄생부터 죽음까지, 여러지점에서 개발자가 작업이 가능하도록 메서드를 오버라이딩 할 수 있게 해준다.
Component 생성 및 마운트 (v16.3 이전 버전)
- constructor
- componentWillMount
- render (최초 랜더)
- componentDidMount // 이 지점에 제일 많이 뭔가를 써놓는다. 호출하고 타이머걸고 메인함수처럼
class App extends React.Component {
_interval;
constructor(props) {
console.log('App constructor');
super(props);
this.state = {
age: 37,
};
}
componentWillMount() {
console.log('App componentWillMount');
}
componentDidMount() {
console.log('App componentDidMount');
this._interval = window.setInterval(() => {
this.setState({
age: this.state.age + 1,
});
}, 1000);
}
componentWillUnmount() {
console.log('App componentWillUnmount');
clearInterval(this._interval);
}
render() {
console.log('App render');
return (
<div>
<h2>
Hello {this.props.name} - {this.state.age}
</h2>
</div>
);
}
}
Component props, state 변경 (v16.3 이전 버전)
- componentWillReceiveProps // 여러 개가 동시에 바뀌었을 때 모아서 한큐에 감. 트랜잭션이라고 볼 수 있겠음
- shouldComponentUpdate // boolean 형태의 값을 리턴해야 함. true면 가고 false 리턴시 안 감. 그래서 최적화를 담당. 최적화 필요 있을때만 오버라이드
- componentWillUpdate
- render
- componentDidUpdate
componentWillReceiveProps(nextProps) {
console.log(
`App componentWillReceiveProps : ${JSON.stringify(nextProps)}`,
);
}
shouldComponentUpdate(nextProps, nextState): boolean {
console.log(
`App shouldComponentUpdate : ${JSON.stringify(
nextProps,
)}, ${JSON.stringify(nextState)}`,
);
return true;
}
componentWillUpdate(nextProps, nextState) {
console.log(
`App componentWillUpdate : ${JSON.stringify(
nextProps,
)}, ${JSON.stringify(nextState)}`,
);
}
componentDidUpdate(prevProps, prevState) {
console.log(
`App componentDidUpdate : ${JSON.stringify(
prevProps,
)}, ${JSON.stringify(prevState)}`,
);
}
componentWillReceiveProps
- props 를 새로 지정했을 때 바로 호출됨
- 여기는 state 의 변경에 반응하지 않음
- 여기서 props 의 값에 따라 state 를 변경해야 한다면,
- setState 를 이용해 state 를 변경함
- 그러면 다음 이벤트로 각각 가는것이 아니라 한번에 변경됨
- 여기서 props 의 값에 따라 state 를 변경해야 한다면,
shouldComponentUpdate
- props 만 변경되어도
- state 만 변경되어도
- props & state 둘다 변경되어도
- newProps 와 new State 를 인자로 해서 호출
- return type 이 boolean임
- true 면 render
- false 면 render 가 호출되지 않습니다.
- 이 함수를 구현하지 않으면, 디폴트는 true
componentWillUpdate
컴포넌트가 재랜더링 되기 직전에 불림 여기선 setState 같은 것을 쓰면 안 됨
componentDidUpdate
컴포넌트가 재랜더링을 마치면 불림
Component 언마운트 (v16.3 이전 버전)
- componentWillUnmpunt
class Button extends React.Component {
componentWillUnmount() {
console.log('Button componentWillUnmount');
}
render() {
return <>hello</>;
}
}
Component 라이프사이클 변경 (v16.3)
- Component 생성 및 마운트 (v16.3)
constructor
componentWillMount => getDerivedStateFromProps
render
componentDidMount
- Component props, state 변경 (v16.3)
componentWillReceiveProps => getDerivedStateFromProps
shouldComponentUpdate
render
componentWillUpdate => getSnapshotBeforeUpdate
(dom에 적용)
componentDidUpdate
- Component 언마운트 (v16.3)
componentWillUnmount
Component 생성 및 마운트 (v16.3)
- constructor
- static getDerivedStateFromProps
- render (최초 랜더)
- componentDidMount
import React from 'react';
class App extends React.Component {
state = {
age: 0,
};
static getDerivedStateFromProps(nextProps, prevState) {
console.log(nextProps, prevState);
if (prevState.age !== nextProps.age) {
return { age: nextProps.age };
}
return null;
}
render() {
console.log('App render');
return <div>{this.state.age}</div>;
}
}
export default App;
Component props, state 변경 (v16.3)
-
static getDerivedStateFromProps (props 변경)
-
shouldComponentUpdate (state 변경)
-
render
-
getSnapshotBeforeUpdate
(dom 에 적용)
-
componentDidUpdate
import React from 'react';
class App extends React.Component {
getSnapshotBeforeUpdate(prevProps, prevState) {
if (prevProps.list.length < this.props.list.length) {
const list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (snapshot !== null) {
const list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}
render() {
console.log('App render');
return <div>{this.state.age}</div>;
}
}
export default App;
Component 언마운트 (v16.3)
- componentWillUnmount
Component 에러 캐치
componentDidCatch
리액트 프로젝트 구성하기
리액트 라우팅 이해하기
- Create React App
- Prettier + ESLint + husky + lint-staged 사용하기
- 리액트 컴포넌트 디버깅
- [과제] tic-tac-toe
- React 의 라우팅 이해하기 (react-router)
- HOC
Create React App
npx create-react-app 프로젝트명
이렇게 만들면 된다. 예를들면 npx create-react-app tic-tac-toe
npx
- npm 5.2.0 이상부터 함께 설치된 커맨드라인 명령어
- 왜 필요?
- 프로젝트의 로컬에 설치된 패키지의 실행 커맨드를 사용하려면,
- package.json 의 npm scripts 에 명령어를 추가하여 사용해야 했다.
- npx 로 바로 실행 가능
- 전역으로 실행하고 싶은 패키지가 있을 경우,
- npm i -g 를 이용하여, 전역에 꼭 설치해서 사용해야 가능했다.
- npx 로 최신 버전의 패키지를 받아 바로 실행 가능
- 프로젝트의 로컬에 설치된 패키지의 실행 커맨드를 사용하려면,
package.json
{
"name": "tic-tac-toe",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-scripts": "3.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
- 리액트 핵심 모듈
- “react”: “^16.9.0”
- “react-dom”: “^16.9.0”
- cra 를 사용하는데 필요한 모듈
- “react-scripts”: “3.1.2”
- npm start
- react-scripts start
- 개발용 서버를 띄운다
- 소스 코드가 수정되었을 때, 다시 컴파일 하고 웹페이지를 새로고침한다.
-
npm run build
- react-scripts build
- Creating an optimized production build…
-
Project 폴더 바로 아래 build 라는 폴더가 만들어지고, 그 안에 Production 배포를 위한 파일들이 생성된다.
- serve 라는 패키지를 전역으로 설치합니다.
- serve 명령어를 -s 옵션으로 build 폴더를 지정하여 실행합니다. -s 옵션은 어떤 라우팅으로 요청해도 index.html 을 응답하도록 합니다.
npm install serve -g serve -s build
- npm test
- react-scripts test
- Jest 를 통해 test code 를 실행한다.
- npm run eject
- react-scripts eject
- eject 를 이용하면, cra 로 만든 프로젝트에서 cra 를 제거
- 보통 cra 내에서 해결이 안되는 설정을 추가해야 할 때 하고, 돌이킬 수 없다.
Prettier + ESLint + husky + lint-staged 사용하기
ESLint
오류라던가, 규칙에 맞지 않는 코드 찾아서 표시해준다.
-
실습
mkdir eslint-test cd eslint-test npm init -y npm install eslint -D npx eslint --init
{ "env": { "commonjs": true, "es6": true, "node": true }, "extends": "eslint:recommended", "globals": { "Atomics": "readonly", "SharedArrayBuffer": "readonly" }, "parserOptions": { "ecmaVersion": 2018 }, "rules": { // rule 추가 "semi": [ "error", "always" ] } }
이후 터미널에서
npx eslint 파일명.js
해보면 찾아주는 걸 볼 수 있음 -
vs코드에서 사용하기
익스텐션에서 다운받고 설정한다{ "name": "tic-tac-toe", "version": "0.1.0", "private": true, "dependencies": { "react": "^16.8.6", "react-dom": "^16.8.6", "react-scripts": "3.0.1" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" }, "eslintConfig": { // 이렇게 "extends": "react-app", "rules": { "semi": [ "error", "always" ] } }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] } }
Prettier
코드 포매터
-
실습
mkdir prettier-test cd prettier-test npm init -y npm i prettier -D
npm prettier 파일명.js
해보면 어떻게 해야하는지 알려준다npm prettier --write 파일명.js
하면 그렇게 교체한다
-
vs코드에서 사용하기
익스텐션에서 다운받고 설정한다 -
Command + Shift + P 누른 후 Format Document
“editor.formatOnSave”: true 하면 저장시 정리해줌
husky
커밋 전에 코드가 예쁘게 되었나 확인해준다
mkdir husky-test
cd husky-test
npm init -y
git init
npm i huskey -D
{
"name": "husky-test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"husky": { // 여기
"hooks": {
"pre-commit": "npm test"
}
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"husky": "^3.0.8"
}
}
React 의 라우팅 이해하기 (react-router)
SPA 라우팅 과정
브라우저에서 최초에 ‘/’ 경로로 요청을 하면,
React Web App 을 내려줍니다.
내려받은 React App 에서 ‘/’ 경로에 맞는 컴포넌트를 보여줍니다.
React App 에서 다른 페이지로 이동하는 동작을 수행하면,
새로운 경로에 맞는 컴포넌트를 보여줍니다.
-
가장 대표적인 라우팅 패키지 react-router-dom
npm i react-router-dom
-
사용법
// src/App.js import React from 'react'; import { BrowserRouter, Route } from 'react-router-dom'; import Home from './pages/Home'; import Profile from './pages/Profile'; import About from './pages/About'; function App() { return ( <BrowserRouter> <Route path="/" component={Home} /> <Route path="/profile" component={Profile} /> <Route path="/about" component={About} /> </BrowserRouter> ); } export default App;
-
Route 컴포넌트에 경로(path) 와 컴포넌트(component) 를 설정하여 나열해줍니다.
-
BrowserRouter 로 Route 들을 감싸줍니다.
-
브라우저에서 요청한 경로에 Route 의 path 가 들어있으면 해당 component 를 보여줍니다.
-
정확히 하려면 exact를 붙인다!!!
동적 라우팅
<Route path="/profile/:id" component={Profile} />
import React from "react"; import { BrowserRouter, Route } from "react-router-dom"; import Home from "./pages/Home"; import Profile from "./pages/Profile"; import About from "./pages/About"; function App() { return ( <BrowserRouter> <Route path="/" exact component={Home} /> <Route path="/profile" exact component={Profile} /> <Route path="/profile/:id" component={Profile} /> <Route path="/about" component={About} /> </BrowserRouter> ); } export default App;
-
props.match.params.id
import React from "react";
export default function Profile(props) {
const id = props.match.params.id;
console.log(id, typeof id);
return (
<div>
<h2>Profile 페이지 입니다.</h2>
{id && <p>id 는 {id} 입니다.</p>}
</div>
);
}