07 Aug 2020
|
react
에듀캐스트 장고&리액트 강의를 듣고 정리하는 글이다.
클래스형 컴포넌트의 한계
- 클래스가 코드 재사용성과 코드 구성을 더 어렵게 만든다.
- 자바스크립트의 this는 다른 언어와 다르게 작동하며, 개발자는 이벤트 핸들러가 등록되는 방법을 기억해야만 한다.
- 부수적으로 작성해야하는 코드가 많다.
- 서로 연관성이 없는 다수 로직을 하나의 생명주기 메서드에서 구현하는 경우가 많다.
- 코드 압축이 잘 안되는 경우가 있고, 컴파일 단계에서 코드 최적화를 어렵게 만든다.
- componentDidMount에서 등록하고 ComponentWillUnmount에서 해제를 깜발할 수 있다. (setInterval같은 함수 해제 깜박하는 등)
함수형 컴포넌트를 사용하자.
함수형 컴포넌트
- 클래스형 컴포넌트에 적용했던 것들을 대부분 적용 가능 (Hook을 활용)
- 현재 리액트 팀에서도 함수형 컴포넌트와 Hook 개발에 집중하고 있다.
- 그래서 리액트를 시작할 때 함수형 컴포넌트를 쓰기를 권장한다.
- 클래스 컴포넌트에 대한 호환성을 보장한다.
다음은 클래스형 컴포넌트 예시인데, 아래처럼 바꿀 수 있다.
클래스형 컴포넌트
class Message1 extends React.Component{
render(){
return (
<div>{this.props.message}</div>
)
}
}
####함수형 컴포넌트
const Message2 = (props) => (
<div>{props.message}</div>
);
const Message3 = ({message}) => (
<div>{message}</div>
);
필수 Hooks (useState, useEffect, userCallback)
- 리액트 버전 16.8에 새로이 추가되었다.
- Hook을 통해 함수형 컴포넌트에서도 상태값과 여러 React의 기능을 활용할 수 있다.
- props, state, context, refs, life-cycle에 대해 보다 직관적인 API를 제공한다.
- 같은 로직을 한 곳으로 모을 수 있어서 가독성에 좋다.
- 클래스형 컴포넌트에서 사용하려면, 커스텀 Wrapper 컴포넌트가 필요하다.
userState 훅
컴포넌트 내에서 상태값을 유지/변경하고 싶을 때 사용한다.
단, 주의해야할 점으로 useState 훅은 이전 상태값을 모두 지우기 때문에, 통으로 변경해주어야 한다.
// 아래처럼 useState 함수를 import 해준다.
import {useState} from "react";
function App2() {
const [value1, setValue1] = useState(0);
const [value2, setValue2] = useState(0);
const [value, setValue] = useState({ value1: 0, value2: 0 }); // 상태값을 바꿀때 다 바꿔줘야한다.
const onClick = () => {
setValue((prevState) => ({ ...prevState, value1: 10 }));
};
return (
<div>
Hello App2
<hr />
{JSON.stringify(value)}
<button onClick={onClick}>Button</button>
</div>
);
}
useEffect 훅
생명주기의 componentDidMount와 componentDidUpdate에 대응한다.
useEffect(() => {}); // render 시에 호출
useEffect(() => {}, []); // mount 시에만 호출
useEffect(() => {}, [value]); // value가 변경될 시에 호출
useEffect(() => {
return () => {};
}, [value]); // unmount 시에 호출된다. clearInterval 같은 곳에 사용
07 Aug 2020
|
react
에듀캐스트 장고&리액트 강의를 듣고 정리하는 글이다.
불변성 (Immutable)
리액트에서는 불변성을 유지하면서, 상탯값을 업데이트해야만 한다.
object는 다음과 같이 처리하여 불변성을 유지할 수 있다.
const todo ={
name: 'immer 이해하기'
is_compelted: false,
};
const newState = {
...todo,
is_compelted: true
}
하지만 배열의 경우에서는 유지하기가 힘들다.
const fruits = ["orange", "apple", "lemon", "banana"]
// 제거된 객체들을 반환하며, fruits 객체를 변경한다.
fruits.splie(1, 2, "strawberry")
// 아래처럼 하면 fruits의 불변성을 유지할 수 있지만, 가독성이나 관리가 쉽지 않다.
const newFruites = [
...fruits.slice(0, 1),
"strawberry",
...fruits.slice(3),
]
// immer를 사용하면, 익숙한 코드로 불변성을 지킬 수 있다.
const newFruites = produce(fruits, draft => {
draft.splice(1, 2, "strawberry");
})
immer 설치
yar add immer
// node.js
const { produce } = require('immer');
// const fruits = ['오렌지', '사과', '레몬', '바나나'];
// const newFruits = produce(fruits, (draft) => {
// draft.splice(1, 2, '딸기');
// });
// console.log(newFruits);
const baseState = [
{
todo: 'Learn ES6+',
done: true,
},
{
todo: 'Try immer',
done: false,
},
];
// const newbaseState = [...baseState, { todo: 'Tweet about it' }];
// newbaseState[1].done = true;
const newbaseState = [
...baseState.map((tweet, index) =>
index === 1 ? { ...tweet, done: true } : tweet,
),
{ todo: 'Tweet about it' },
];
const newimmerState = produce(baseState, (draft) => {
draft[1].done = true;
draft.push({ todo: 'Tweet about it' });
});
console.log(baseState);
console.log(newbaseState);
console.log(newimmerState);
07 Aug 2020
|
react
에듀캐스트 장고&리액트 강의를 듣고 정리하는 글이다.
간단한 TODO List 만들어 보기
import React from 'react';
import { List, Input } from 'antd';
// class TodoItem extends React.Component {
// render() {
// const { todo } = this.props;
// return <li>{todo}</li>;
// }
// }
const TodoItem = ({ todo }) => <li>{todo}</li>;
class ToDoList extends React.Component {
state = {
todoList: ['파이썬 익히기', '장고 익히기'],
current: '',
};
onChange = (e) => {
const { value } = e.target;
this.setState({
current: value,
});
};
onKeyDown = (e) => {
if (e.keyCode === 13) {
// ENTER KEY
const { todoList, current } = this.state;
if (current.trim().length > 0) {
this.setState({
current: '',
todoList: [...todoList, current.trim()], // state는 불변으로 유지하는게 좋으니 복사 사용
});
}
}
};
render() {
return (
<div style=>
<List
header={'Todo List'}
dataSource={this.state.todoList}
bordered={true}
renderItem={(todo) => <List.Item>{todo}</List.Item>}
style=
/>
<Input
type="text"
value={this.state.current}
placeholder={'할일을 입력해주세요.'}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
/>
{/* <ul>
{this.state.todoList.map((todo, index) => (
<TodoItem key={index} todo={todo} /> // 순회돌면 key 필요
))}
</ul>
<input
type="text"
placeholder="할일을 입력해주세요."
value={this.state.current}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
/>
<hr /> */}
{/* {JSON.stringify(this.state)} */}
</div>
);
}
}
export default ToDoList;
07 Aug 2020
|
react
에듀캐스트 장고&리액트 강의를 듣고 정리하는 글이다.
이벤트
컴포넌트에는 여러 이벤트가 발생 -> 이벤트에 대한 처리를 커스텀
웹브라우저의 HTML이벤트를 기본적으로 지원
- 이벤트 핸들러 속성명은 camelCase로만 작성 (HTML에서는 onclick, 리액트는 onClick)
- 이벤트 핸들러에는 필히 함수를 지정 (HTML에서는 문자열로 코드를 지정)
DOM 요소에만 이벤트가 지원한다.
- 커스텀 리액트 컴포넌트에서는 HTML 이벤트를 지원하지 않는다.
- 하지만 내부 Element에 DOM요소를 담아, 핸들러를 지정할 수 있다.
import React from 'react';
import PropTypes from 'prop-types';
import Counter from 'Counter';
import 'App.css';
class App extends React.Component {
state = {
myquery: '',
language: '',
};
onChange = (e) => {
const { name, value } = e.target;
this.setState({
[name]: value,
});
};
render() {
return (
<>
<Counter onClick={() => console.log('clicked')} />
<input name="myquery" onChange={this.onChange} />
<input name="language" onChange={this.onChange} />
<hr></hr>
{JSON.stringify(this.state)}
</>
);
}
}
export default App;
리액트 컴포넌트 만들기 (클릭 카운터)
App.js
import React from 'react';
import PropTypes from 'prop-types';
import Counter from 'Counter';
import 'App.css';
class App extends React.Component {
render() {
return (
<div>
<Counter />
<Counter color="green" />
<Counter color="blue" />
</div>
);
}
}
export default App;
Counter.js
- defaultPros: 디폴트 속성값 정해준다.
- proTypes: 속성값은 자료형을 정해준다.
import React from 'react';
import PropTypes from 'prop-types';
class Counter extends React.Component {
static defaultProps = {
color: 'red',
};
static proTypes = {
color: PropTypes.string,
};
state = {
color: this.props.color,
value: 0,
};
onClick = () => {
// this.setState({ value: this.state.value } + 1);
this.setState(({ value: prevValue }) => ({
value: prevValue + 1,
}));
};
// 우클릭 이벤트
onContextMenu = (e) => {
e.preventDefault();
this.setState(({ value: prevValue }) => ({
value: prevValue >= 1 ? prevValue - 1 : 0, // 삼항 연산자 참이면 좌측 실행, 거짓이면 우측 실행
}));
};
render() {
const { color, value } = this.state;
return (
<div
onClick={this.onClick}
onContextMenu={this.onContextMenu}
style=
>
{value}
</div>
);
}
}
const style = {
width: '100px',
height: '100px',
display: 'inline-block',
borderRadius: '50px',
textAlign: 'center',
lineHeight: '100px',
userSelect: 'none',
fontSize: '3rem',
margin: '1rem',
};
export default Counter;