react react Hooks
업데이트:
카테고리: react
/태그: lifecycle, useCallback, useMemo, 구조분해할당
React Hooks
- class 없이 state를 사용하는 기능
react ⇒ class components
- 많은 기능 제공
- 긴 코드 양
- 복잡한 코드
- 더딘 성능
react hooks ⇒ functional components
- 적은 기능 제공
- 짧은 코드 양
- 심플한 코드
- 빠른 성능
클래스 컴포넌트에서만 사용할 수 있는 기능은?
react lifecycle
- 시작했을 때, 업데이트 되었을 떄, 사용하지 않을 때 작동하는 함수
- hook이 업데이트 되면서 함수형 컴포넌트에도 사용 가능해졌다.
useEffect
를 이용하여 처리
// 변수와 해당 변수를 변경할 함수를 정의
const [Name, setName] = useState("")
useEffect(() => {
Axios.get(..)
.then(res => {
setName(res)
}
})
⇒ 코드가 간결해진다.
HOC(High Order Component)
컴포넌트를 인자로 받아서 새로운 리액트 컴포넌트를 리턴하는 함수
화면에서 재사용이 가능한 로직만을 분리하여 component로 만들고 재사용 불가능한 것은 인자로 받아오는 것
HOC 컴포넌트를 이용하여 Wrapper 컴포넌트를 줄이게됨
작성하다보면 HOC 도 너무 많아 지게됨!
이때 사용하는 것이 custom react hook
custom react hook
위 두 코드는 동일한 코드
HOC는 감사줘야 사용을 할 수 있지만, CRH은 감싸주지 않아도 사용이 가능하다!
클래스 ⇒ 함수형
- 컴포넌트 자체를 바꾸기
- return 안에 작성
-
state 를 useState hook을 이용하여 표현
state = { todoData : [ { id: "1", title: "공부하기", completed: true, }, { id: "2", title: "청소하기", completed: false, }, ], value : "", };
import React, {useState} from "react"; // 변수 이름, 변수를 변경하는 함수, 초기값 const [todoData, setTodoData] = useState([]); const [value, setValue] = useState("");
- this.state.todoData ⇒ todoData
-
this.setState({value: “”}) ⇒ setValue(””)
// 이전 값에 추가하기 // prev : Todo의 값이 들어가 있다. setTodoData(prev => [...prev, newTodo]);
- 변수 앞에
const
,let
작성 - 정의된 함수 및 메소드에
this
제거
State 와 Props
State : 해당 컴포넌트 내에서만 사용
Props : 부모 컴포넌트에서 자식 컴포넌트로 데이터 전달
⇒ 데이터가 변경되면 안됨
// 이런식으로 자식에게 데이터 송출
<ChildCompoennets message={message} currentNumber={currentNumber}/>
// 자식은 해당 형식으로 받는다.
export default function ChildComponents({message}) {
}
구조분해할당
배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JS 표현식
let {accessory, animal, color, hairType} = animalData;
const student = {
firstName: 'John',
lastName: 'Smith',
}
// firstName을 재정의할 수 있다.
// 추후 첫번째 이름은 fName으로 호출 가능
const { firstName: fName = 'not given', lastName} = student;
TailWindCSS
html 안에서 css 스타일을 만들어 주게하는 css 프레임워크
⇒ css 프레임워크 : 소요되는 시간을 최소화 하기위해 프로젝트에 적용할 수 있는 css 파일 모음집
- 부트스트랩과 비슷하게 미리 세팅된 Utility Class 를 이용
- 빠른 스타일링 가능
- class 혹은 id 명을 작성하기 위한 고생을 하지 않아도 됨
설치
npm install -D tailwindcss
⇒ D는 개발자 모드에만 작동됨
npx tailwindcss init
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{html,js}"],
theme: {
extend: {},
},
plugins: [],
}
// input.css
@tailwind base;
@tailwind components;
@tailwind utilities;
Drag and Drop 구현
사용자가 원하는 대로 순서를 변하게 한다.
- 사용자가 드래그 할때의 애니메이션
- 드래그를 멈췄는지 확인
- 재정렬한 경우 항목의 위치를 새 항목으로 업데이트
react-beautiful-dnd 를 사용하여 실행
npm install react-beautiful-dnd —save
import { DragDropContext, Dropaable, Draggable } from 'react-beautiful-dnd';
DragDropContext
: 드래그와 드롭할 컨텍스트를 감싸준다.Droppable
: 놓을 자리로 Draggable을 포함하고 있어야한다Draggable
: 드래그 할 대상
const handleEnd = (result) => {
// drag 이벤트 정보가 포함
console.log('result', result)
// 목적지가 없다면 제거
if (!result.destination) return ;
const newTodoData = todoData;
// 변경 시킬려는 아이템 값을 제거
const [reordredItem] = newTodoData.splice(result.source.index, 1);
// 변경위치에 넣어준다.
newTodoData.splice(result.destination.index, 0, reordredItem);
setTodoData(newTodoData);
};
<div>
// 전체적으로 이동이 일어날 div
// onDragEnd: 드래그가 끝났을떄 실행될 함수
<DragDropContext onDragEnd={handleEnd}>
// droppable로 한번 더 감싸준다.
<Droppable droppableId="todo">
{(provided) => (
<div {...provided.droppableProps} ref={provided.innerRef}>
{todoData.map((data, index) => (
<Draggable
// map을 사용하여 넣었기 떄문에 key를 입력
key={data.id}
draggableId={data.id.toString()}
index={index}
>
// snapshot은 추후에 스타일링으로 사용
{(provided, snapshot) => (
<div key={data.id} {...provided.draggableProps} ref={provided.innerRef} {...provided.dragHandleProps}
// 드래그 중일 때 색이 변하도록
className={`${snapshot.isDragging ? "bg-gray-400": "bg-gray-100"} flex items-center justify-between w-full px-4 py-1 my-2 text-gray-600 bg-gray-100 rounded`}
>
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
</div>
-
Invariant failed: Cannot find droppable entry with id [todo]
index.js
에서<React.StrictMode></React.StrictMode>
부분 삭제
리액트앱 최적화
리액트 불변성 지키기
상태를 변경할 수 없는 것을 의미
- 원시 타입(불변성 o): Boolean, String, null, Number, undefined, Symbol
- 메모리에 실제 데이터가 변수가 저장
- 참조 타입(불변성 x): Object, Array
- 데이터 크기가 저장되어 있지 않음 데이터 값은 heap에
heap주소
가 메모리에 저장 - 새로운 배열을 반환하는 메소드를 사용 (spread operator, map, slice, filter, reduce)
- 데이터 크기가 저장되어 있지 않음 데이터 값은 heap에
컴포넌트 랜더링 최적화
Todo 앱이 App, Form, Lists, List로 나눠져 있다. 나눠준 이유는 재사용성, 랜더링 최적화를 위해서이다
Form에서 입력이 일어나면 모든 컴포넌트가 다시 랜더링 되게된다.
React.memo
를 이용해서 최적할 수 있다.
리액트는 리랜더링 후 랜더링 전과 데이터를 비교하여 데이터가 다르면 DOM을 업데이트 한다.
React.memo
는 컴포넌트 랜더링 결과를 저장하고 props가 같다면 저장된 결과를 재사용하여 랜더링 횟수를 줄인다.
함수형 컴포넌트는 arrow 함수 형태로 작성하여 아래와 같은 형식으로 작성
const Lists = React.memo(({todoData, setTodoData}) => {})
export default Lists;
마자막 줄에 export default
를 작성해주어야 한다.
useCallback을 이용한 함수 최적화
컴포넌트가 리랜더링 될때 함수도 다시 제작하게 된다.
// 앱에 존재하는 handle 함수는 Lists를 거쳐
// 최종적으로 List 컴포넌트에서 실행된다.
// useCallback을 사용하면
const handleClick = useCallback((id) => {
let newTodoData = todoData.filter(data => data.id !== id);
setTodoData(newTodoData);
// 안에 있는 데이터(todoData)가 변하지 않으면 함수가 새로 생성되지 않는다.
}, [todoData]);
useMemo을 이용한 값 최적화
Compute 내에 복잡한 함수가 존재하여 오래걸린다면 리랜더링 될 때마다 연산수행에 시간에 걸리게 되고 UI 지연현상이 일어나게 된다.
이전에 계산해두었던 값을 저장하여 변경이 없다면 전에 저장해놓았던 값을 사용한다.
const result = useMemo(() => compute(a,b) [a,b]);
기존 데이터 수정하기
수정상태인지 저장하는 변수 / 수정데이터를 저장하는 변수
const [isEditing, setIsEditing] = useState(false);
const [editedTitle, setEditTitle] = useState(title);
- 수정버튼 클릭시 수정상태로 변화
- 수정할 폼 랜더링
- if 문으로 랜더링할 태그를 설정함
- 수정완료후에는 바뀐 데이터로 변환
localStorage에 todoData 담기
localStorage에 데이터 담아두기
이때 JSON.stringify
를 이용하여 텍스트로 반환후 저장, 변환하지 않으면 Object Object
형식으로 나오게 된다.
localStorage.setItem("todoData", JSON.stringify(newTodoData));
가져올때는 아래 형식으로 localStorage.parse
를 통해 가져온다.
const initialTodoData = localStorage.getItem("todoData") ? JSON.parse(localStorage.getItem("todoData")) : [];