오늘은 29. 1장 | 벨로퍼트와 함께 하는 모던 리액트 : 리액트 입문 부분에서
14개의 클립을 듣고 완강했다
<context API를 사용한 전역 값 관리>
*
UserList는 함수의 중간다리 역할, 해당 함수를 직접 사용하는 일도 없음
특정 함수를 특정 컴포넌트를 거쳐서 원하는 컴포넌트에게 전달하는 작업을
리액트에서 간단하게 하려면?
(1) Context API (2) dispatch
*
Context API : 프로젝트 안에서 전역적으로 사용 할 수 있는 값을 관리 할 수 있음
값 : 상태, 함수, 라이브러리 인스턴스, DOM…
*
Context API 함수 : React.createContext()
(예)const UserDispatch = React.createContext(null);
createContext 의 파라미터에는 Context 의 기본값을 설정
(Context 를 쓸 때 값을 따로 지정하지 않을 경우 사용되는 기본 값)
Context 안에 Provider 라는 컴포넌트가 들어있는데
이 컴포넌트를 통하여 Context 의 값을 정할 수 있고 사용시 value 값 설정
(예)<UserDispatch.Provider value={dispatch}>...</UserDispatch.Provider>
<UserDispatch Context만들기>
*
UserDispatch 를 만들 때 다음과 같이 내보내주는 작업을 하고
(예)export const UserDispatch = React.createContext(null);
이렇게 내보내주면 나중에 사용하고 싶을 때 다음과 같이 불러와서 사용 할 수 있음
(예)import { UserDispatch } from './App';
*
Context 다 만들면,
App 에서 onToggle 과 onRemove 를 지우기
UserList 에게 props를 전달하는것도 지우기
.UserList 컴포넌트에서 에서 onToggle 과 onRemove 와 관련된 코드들을 지우기
*
User 컴포넌트에서 dispatch를 사용하기 위해서 useContext Hook 을 사용하여 UserDispatch Context 를 조회
(예)
import React, { useContext } from 'react';
import { UserDispatch } from './App';
const User = React.memo(function User({ user }) {
const dispatch = useContext(UserDispatch);
return (
<div>
<b
style={{
cursor: 'pointer',
color: user.active ? 'green' : 'black'
}}
onClick={() => {
dispatch({ type: 'TOGGLE_USER', id: user.id });
}}
>
{user.username}
</b>
<span>({user.email})</span>
<button
onClick={() => {
dispatch({ type: 'REMOVE_USER', id: user.id });
}}
>
삭제
</button>
</div>
);
});
function UserList({ users }) {
return (
<div>
{users.map(user => (
<User user={user} key={user.id} />
))}
</div>
);
}
export default React.memo(UserList);
<Immer를 사용한 더 쉬운 불변성 지키기>
*
리액트에서 배열이나 객체를 업데이트 해야 할 때에는 직접 수정 하면 안됨
불변성을 지켜주면서 업데이트를 해야함
(예)연산자를 사용해서 새로운 객체 만들기
const object = {
a: 1,
b: 2
};
const nextObject = {
...object,
b: 3
};
*
배열도 마찬가지로, push, splice 등의 함수를 사용, n 번째 항목을 직접 수정하면 안되고
다음과 같이 concat, filter, map 등의 함수를 사용해야 함
*
데이터의 구조가 조금 까다로워지면 불변성을 지키며 새 데이터를 생성해 내는 코드가 복잡해짐
(예) 기존의 방법
const state = {
posts: [
{
id: 1,
title: '제목입니다.',
body: '내용입니다.',
comments: [
{
id: 1,
text: '와 정말 잘 읽었습니다.'
}
]
},
{
id: 2,
title: '제목입니다.',
body: '내용입니다.',
comments: [
{
id: 2,
text: '또 다른 댓글 어쩌고 저쩌고'
}
]
}
],
selectedId: 1
};
여기서 posts 배열 안의 id 가 1 인 post 객체를 찾아서,
comments 에 새로운 댓글 객체를 추가해줘야 한다면
const nextState = {
...state,
posts: state.posts.map(post =>
post.id === 1
? {
...post,
comments: post.comments.concat({
id: 3,
text: '새로운 댓글'
})
}
: post
)
};
(예) immer 라는 라이브러리를 사용한 예
const nextState = produce(state, draft => {
const post = draft.posts.find(post => post.id === 1);
post.comments.push({
id: 3,
text: '와 정말 쉽다!'
});
});
*
Immer : 상태를 업데이트 할 때, 불변성을 신경쓰지 않으면서 Immer 가 불변성 관리를 대신 함
*
Immer 사용법
(1)프로젝트에서 다음 명령어를 실행하여 Immer 를 설치 : $ yarn add immer
(2)코드의 상단에서 immer 를 불러오기 : import produce from 'immer';
(3)첫번째 파라미터 : 수정하고 싶은 상태, 두번째 파라미터 : 어떻게 업데이트하고 싶을지 정의하는 함수
<리듀서 Immer로 구현하기>
Immer 를 사용해서 간단해지는 업데이트가 있고, 오히려 코드가 길어지는 업데이트 들이 있음
TOGGLE_USER 액션의 경우엔 확실히 Immer 를 사용하니 코드가 깔끔
*
Immer 와 함수형 업데이트
(예)
const [todo, setTodo] = useState({
text: 'Hello',
done: false
});
const onClick = useCallback(() => {
setTodo(todo => ({ //업데이트를 해주는 함수를 넣으면 useCallback 을 사용하는 경우
...todo, 두번째 파라미터인 deps 배열에 todo 를 넣지 않아도 됨
done: !todo.done
}));
}, []);
이렇게 함수형 업데이트를 하는 경우에, Immer 를 사용하면 상황에 따라 더 편하게 코드를 작성 할 수 있음
만약에 produce 함수에 두개의 파라미터를 넣게 된다면,
첫번째 파라미터에 넣은 상태를 불변성을 유지하면서 새로운 상태를 만들어주지만,
만약에 첫번째 파라미터를 생략하고 바로 업데이트 함수를 넣어주게 된다면,
반환 값은 새로운 상태가 아닌 상태를 업데이트 해주는 함수가 됨
*
Immer 은 분명히 정말 편하지만 성능적으로는 Immer 를 사용하지 않은 코드가 조금 더 빠르다
필요한곳에만 쓰고, 간단히 처리 될 수 있을 때 쓸 것
<클래스형 컴포넌트 소개>
*
클래스형 컴포넌트를 사용하는 경우는 흔히 없음, 유지보수와 몇 개의 작업 뿐…
*
클래스형 컴포넌트를 만드는 방법
(예)
Hello.js
import React, { Component } from 'react';
class Hello extends Component {
render() { //클래스형 컴포넌트에서는 render() 메서드가 꼭 있어야 렌더링하고 싶은 JSX 를 반환,
const { color, name, isSpecial } = this.props; //props 를 조회시 this.props 를 조회
return (
<div style={{ color }}>
{isSpecial && <b>*</b>}
안녕하세요 {name}
</div>
);
}
}
Hello.defaultProps = { //defaultProps 함수형 컴포넌트 or 클래스 내부에 static 키워드와 함께 선언
name: '이름없음'
};
export default Hello;
*
커스텀 메서드 만들기 : 컴포넌트 안에 선언
(예)
const onIncrease = () => {
dispatch({ type: 'INCREMENT' });
};
*
클래스형 컴포넌트에서는 커스텀 메서드 선언
: render 함수 내부에서 선언은 할 수 있기는 있으나 일반적으로 클래스 안에 선언
*
클래스 내부에 종속된 함수 : "메서드"
클래스에서 커스텀 메서드를 만들게 될 때에는 보통 이름을 handle...로, 정해진 바는 아님
추후 상태를 업데이트시 : 메서드에서 this.setState 라는 함수를 사용
*
메서드와 컴포넌트 인스턴스의 관계가 끊기지 않게 하려면 3가지 방법
(1)클래스의 생성자 메서드 constructor 에서 bind 작업을 해주는 것 입니다.
함수의 bind 를 사용하면, 해당 함수에서 가르킬 this 를 직접 설정해줄 수 있음
constructor 에서는 props 파라미터로 받아오고 super(props) 를 호출
(클래스가 컴포넌트로서 작동 할 수 있게 하는 Component 쪽에 구현되어있는
생성자 함수를 먼저 실행해주고, 우리가 할 작업을 하겠다 라는 것을 의미)
(2)커스텀 메서드를 선언 할 때 화살표 함수 문법을 사용해서 작성하는 것
CRA 로 만든 프로젝트에서는 커스텀 메서드를 만들 때 이 방법을 많이 사용
(3)onClick 에서 새로운 함수를 만들어서 전달 : 렌더링 할 때마다 함수가 새로 만들어져서 잘 안씀
<클래스형 컴포넌트의 state와 set state>
클래스형 컴포넌트에서 상태를 관리 할 때에는 state 라는 것을 사용
state 를 선언 할 때에는 constructor 내부에서 this.state 를 설정
클래스형 컴포넌트의 state 는 무조건 객체형태여야, render 메서드에서 state 를 조회시 this.state 를 조회
(예)
Counter.js
import React, { Component } from 'react';
class Counter extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0
};
}
handleIncrease = () => {
console.log('increase');
console.log(this);
};
handleDecrease = () => {
console.log('decrease');
};
render() {
return (
<div>
<h1>{this.state.counter}</h1>
<button onClick={this.handleIncrease}>+1</button>
<button onClick={this.handleDecrease}>-1</button>
</div>
);
}
}
export default Counter;
(예) 화살표 함수 문법 class-properties 문법이 적용되어 있다면 constructor 없어도 다음과 같이 state를 설정
Counter.js
import React, { Component } from 'react';
class Counter extends Component {
state = {
counter: 0
};
handleIncrease = () => {
console.log('increase');
console.log(this);
};
handleDecrease = () => {
console.log('decrease');
};
render() {
return (
<div>
<h1>{this.state.counter}</h1>
<button onClick={this.handleIncrease}>+1</button>
<button onClick={this.handleDecrease}>-1</button>
</div>
);
}
}
export default Counter;
*
상태 업데이트하기: this.setState 함수를 사용하면 됩니다.
Counter.js
import React, { Component } from 'react';
class Counter extends Component {
state = {
counter: 0
};
handleIncrease = () => {
this.setState({
counter: this.state.counter + 1
});
};
handleDecrease = () => {
this.setState({
counter: this.state.counter - 1
});
};
render() {
return (
<div>
<h1>{this.state.counter}</h1>
<button onClick={this.handleIncrease}>+1</button>
<button onClick={this.handleDecrease}>-1</button>
</div>
);
}
}
export default Counter;
*
this.setState 를 할 때 파라미터에 넣는 객체에 fixed 값을 넣어주지 않아도 값이 유지
클래스형 컴포넌트의 state 에서 객체 형태의 상태를 관리해야 한다면, 불변성을 관리하며 업데이트 필요
*
setState 의 함수형 업데이트
(예)
import React, { Component } from 'react';
class Counter extends Component {
state = {
counter: 0,
fixed: 1
};
handleIncrease = () => {
this.setState(state => ({
counter: state.counter + 1
}));
};
handleDecrease = () => {
this.setState(state => ({
counter: state.counter - 1
}));
};
render() {
return (
<div>
<h1>{this.state.counter}</h1>
<button onClick={this.handleIncrease}>+1</button>
<button onClick={this.handleDecrease}>-1</button>
<p>고정된 값: {this.state.fixed}</p>
</div>
);
}
}
export default Counter;
*
함수형 업데이트는 보통 한 함수에서 setState 를 여러번에 걸쳐서 해야 되는 경우에 사용하면 유용
setState 는 단순히 상태를 바꾸는 함수가 아니라 상태로 바꿔달라고 요청해주는 함수로 이해해야함
성능적인 이유 때문에 리액트에서는 상태가 바로 업데이트 되지 않고 비동기적으로 업데이트가 됨
상태가 업데이트 되고 나서 어떤 작업을 하고 싶다면 다음과 같이 setState 의 두번째 파라미터에 콜백함수
<lifecycle 메서드>
생명주기 메서드 : 컴포넌트가 브라우저상에 나타나고, 업데이트되고, 사라지게 될 때 호출되는 메서드들
(+)컴포넌트에서 에러가 났을 때 호출되는 메서드
클래스형 컴포넌트에서만 사용 할 수 있음, 중요도 떨어지니 크게 신경쓰지 말 것
*
<마운트>
constructor
getDerivedStateFromProps
render
componentDidMount
constructor : 컴포넌트의 생성자 메서드, 컴포넌트가 만들어지면 가장 먼저 실행.
constructor(props) {
super(props);
console.log("constructor");
}
getDerivedStateFromProps : props 로 받아온 것을 state 에 넣어주고 싶을 때
앞에 static 을 필요로 하고, 이 안에서는 this 롤 조회 할 수 없음
특정 객체를 반환하게 되면 객체 안에 있는 내용들이 컴포넌트의 state 로 설정
반면 null 을 반환하게 되면 아무 일도 발생하지 않음
컴포넌트가 처음 렌더링 되기 전에도 호출 되고, 그 이후 리렌더링 되기 전에도 매번 실행
static getDerivedStateFromProps(nextProps, prevState) {
console.log("getDerivedStateFromProps");
if (nextProps.color !== prevState.color) {
return { color: nextProps.color };
}
return null;
}
render : 컴포넌트를 렌더링하는 메서드
componentDidMount : 컴포넌트의 첫번째 렌더링이 마치고 나면 호출되는 메서드
이 메서드가 호출되는 시점에는 만든 컴포넌트가 화면에 나타난 상태
*
<업데이트> : 컴포넌트가 업데이트 되는 시점 생명주기 메서드
getDerivedStateFromProps
shouldComponentUpdate
render
getSnapshotBeforeUpdate
componentDidUpdate
getDerivedStateFromProps
shouldComponentUpdate : 컴포넌트가 리렌더링 할지 말지를 결정하는 메서드, 최적화 할 때
shouldComponentUpdate(nextProps, nextState) {
console.log("shouldComponentUpdate", nextProps, nextState);
// 숫자의 마지막 자리가 4면 리렌더링하지 않습니다
return nextState.number % 10 !== 4;
}
render
getSnapshotBeforeUpdate : 컴포넌트에 변화가 일어나기 직전의 DOM 상태를 가져와서 특정 값을 반환하면 그 다음 발생하게 되는 componentDidUpdate 함수에서 받아와서 사용을 할 수 있음
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("getSnapshotBeforeUpdate");
if (prevProps.color !== this.props.color) {
return this.myRef.style.color;
}
return null;
}
componentDidUpdate : 리렌더링이 마치고, 화면에 우리가 원하는 변화가 모두 반영되고 난 뒤 호출되는 메서드,
3번째 파라미터로 getSnapshotBeforeUpdate 에서 반환한 값을 조회 할 수 있음
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("componentDidUpdate", prevProps, prevState);
if (snapshot) {
console.log("업데이트 되기 직전 색상: ", snapshot);
}
}
getSnapshotBeforeUpdate (안중요)
*
<언마운트> : 컴포넌트가 화면에서 사라지는것을 의미
componentWillUnmount 컴포넌트가 화면에서 사라지기 직전에 호출
<componentDidCatch 메서드>
*
componentDidCatch 사용하여 사전에 예외처리를 하지 않은 에러가 발생 했을 때 사용자에게 에러가 발생했다고 알려주는 화면을 만들기
*
componentDidCatch 메서드 첫번째 파라미터 : 에러의 내용, 두번째 파라미터 : 에러 발생 위치
<componentDidCatch와 sentry 연동>
componentDidCatch 가 실제로 호출되는 일은 서비스에서 없어야함,
sentry라는 상용서비스가 있음
<prettier>
자동으로 코드의 스타일을 관리해주는 도구
코드의 스타일을 여러분의 마음대로 쉽게 커스터마이징 할 수 있음
자바스크립트, HTML, CSS 코드의 코드 스타일을 관리
React, Angular, Vue 등의 라이브러리도 지원, 플러그인을 통하여 다른 언어도 관리
*
기본적인 사용 방법은 명령어를 사용,
Git을 통해 commit 할 때마다 자동으로 실행되도록 설정 가능
에디터와 연동해서 사용하는 것 추천
*
기본 설정
- trailingComma: "none", "es5", "all" 으로 설정을 할 수 있는데, 객체 또는 배열이 여러줄로 구성되어 있으면 다음과 같이 맨 마지막 줄에 쉼표를 붙여줌
"none" 이면 쉼표를 붙이지 않고,
"es5" 이면 객체, 배열을 사용하게 될 떄 쉼표를 붙이고,
"all" 이면 함수를 사용 할 때 인자를 전달 할 때도 쉼표를 붙입니다.
- tabWidth: 들여쓰기의 크기
- semi: 세미콜론 (;) 을 쓸지 말지, 사용하고 싶다면 true 로 설정
- singleQuote: 문자열을 입력 할 때 " 를 쓸지 ' 를 쓸지, " 만 쓰고 싶다면 false 로 설정
<ESLint 소개>
자바스크립트의 문법을 확인해주는 도구
<ESLint 설정, 커스터마이징>
ESLint 에 airbnb / standard / google 같은 설정을 적용하게 되면 굉장히 까다로움
여럿이서 협업하는 프로젝트가 아니라면, 기본 설정 적용 추천
<Snippet 사용하기>
에디터마다 내장되어있는 기능 ("코드 조각")
자주 사용되는 코드에 대하여 단축어를 만들어서 코드를 빠르게 생성해내는 것
프론트엔드 개발 올인원 패키지 with React Online. | 패스트캠퍼스
프론트엔드 개발 러닝패스, 이 강의 패키지 하나로 끝낸다!
www.fastcampus.co.kr
'패스트캠퍼스 챌린지 < 프론트엔드-react>' 카테고리의 다른 글
[패스트캠퍼스 수강후기] 프론트엔드 강의 36회차 미션 (0) | 2020.08.03 |
---|---|
[패스트캠퍼스 수강후기] 프론트엔드 강의 35회차 미션 (0) | 2020.08.02 |
[패스트캠퍼스 수강후기] 프론트엔드 강의 33회차 미션 (0) | 2020.07.31 |
[패스트캠퍼스 수강후기] 프론트엔드 강의 32회차 미션 (0) | 2020.07.30 |
[패스트캠퍼스 수강후기] 프론트엔드 강의 31회차 미션 (0) | 2020.07.29 |