[react] Tailwind css, react-beautiful-dnd, 불변성, React.memo, useCallback, useMemo, localStorage
Tailwind CSS
Tailwind CSS - Rapidly build modern websites without ever leaving your HTML.
- 장점
Tailwind CSS는 부트스트랩과 비슷하게 m-1, flex와 같이 미리 세팅된 Uitility Class를 활용하는 방식으로 HTML에서 스타일링 할 수 있다.
- 빠른 스타일링 작업이 가능
- class 혹은 id명을 작성하기 위한 고생을 하지 않아도 된다.
- 유틸리티 클래스가 익숙해지는 시간이 필요할 수 있지만 IntelliSense 플러그인이 제공돼서 금방 익숙해질 수 있다.
react-beautiful-dnd
리액트에서 드래그앤드랍에 대한 기능을 제공하는 npm 라이브러리
https://github.com/atlassian/react-beautiful-dnd
const handleEnd = (result) => {
if (!result.destination) return;
const newTodoData = todoData;
// 1. 변경시키는 아이템을 배열에서 삭제
// 2. return 값으로 지워진 아이템을 잡아줌
const [reorderedItem] = newTodoData.splice(result.source.index, 1);
// 원하는 자리에 reorderItem을 insert
newTodoData.splice(result.destination.index, 0, reorderedItem);
settodoData(newTodoData);
};
return (
<div>
<DragDropContext onDragEnd={handleEnd}>
<Droppable droppableId="todo">
{(provided) => (
<div {...provided.droppableProps} ref={provided.innerRef}>
{todoData.map((data, index) => (
<Draggable
key={data.id}
draggableId={data.id.toString()}
index={index}
>
{(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 border rounded`}
>
...
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
</div>
);
}
provided object는 스타일 지정 및 조회를 위한 속성이 포함
사용자가 요소를 드래그하는 경우 className 속성을 selected로 변경한다.
placeholder 속성은 목록에 빈 공간을 만든다. 이를 통해 드래그 작업이 자연스럽게 느껴지도록 함.
리액트 불변성 지키기
기본적으로 Javascript는 원시 타입에 대한 참조 및 값을 저장하기 위해 Call Stack 메모리 공간을 사용한다. 하지만 참조 타입의 경우 Heap이라는 별도의 메모리 공간을 사용한다. 이 경우 Call Stack은 개체 및 배열 값이 아닌 메모리에만 Heap 메모리 참조 ID를 값으로 지정한다.
- 원시 타입
let animal = 'cat';
animal = 'dog';
animal cat을 dog로 대체한 것이 아니라 메모리 영역 a에 할당된 cat을 그대로 두고 b에 할당된 dog를 새로 할당한 것이다.
→ 불변성을 가지고 있어 리액트에서 따로 처리할 필요가 없음.
- 참조 타입
let arr = ['a', 'b', 'c'];
arr = ['d', 'e', 'f'];
배열에 대한 요소를 추가하거나 객체 속성 값을 변경할 때 Call Stack의 참조 ID는 동일하게 유지되고 Heap 메모리에서만 변경된다.
→ 불변성이 유지되지 않기 때문에 리액트에서 따로 처리를 해주어야 한다.
불변성을 지켜야 하는 이유
- 참조 타입에서 객체나 배열 값이 변할 때 원본 데이터가 변경되기 때문에 이 원본 데이터를 참조하고 있는 다른 객체에서 예상치 못한 오류가 발생할 수 있다.
- 리액트에서 화면을 업데이트할 때 불변성을 지켜서 값을 이전 값과 비교해서 변경된 사항을 확인해 업데이트하기 때문에 불변성을 지켜줘야 한다.
불변성을 지키는 방법
아예 새로운 배열을 반환하는 메서드를 사용한다.
spread operator, map, filter, slice, reduce
원본 데이터를 변경하는 메서드: splice, push
React.memo를 이용한 컴포넌트 렌더링 최적화
어떤 동작마다 렌더링하지 않아도 되는 컴포넌트까지 리렌더링 되는 경우가 있다.
원하는 컴포넌트를 React.memo 또는 memo 를 불러와 감싸준다.
useCallback을 이용한 함수 최적화
컴포넌트가 렌더링될 때 그 안에 있는 함수도 다시 만든다.
하지만 똑같은 함수를 계속 다시 만드는 것은 좋은 현상이 아니다.
그리고 이렇게 되면 이 함수가 자식 컴포넌트에 props를 내려줄 때 함수를 포함하고 있는 컴포넌트가 리렌더링 될 때마다 자식 컴포넌트도 함수가 새롭게 만들어지니 계속 리렌더링 하게 된다.
useCallback 적용은 useCallback 안에 콜백 함수와 의존성 배열을 순서대로 넣어주면 된다.
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
함수 내에서 참조하는 state, props가 있다면 의존성 배열에 추가하면 된다.
useCallback으로 인해서 [a, b]가 변하지 않는다면 함수는 새로 생성되지 않는다.
새로 생성되지 않기 때문에 메모리에 새로 할당되지 않고 동일 참조 값을 사용하게 된다.
의존성 배열에 아무것도 없다면 컴포넌트가 최초 렌더링 시에만 함수가 생성되며 그 이후에는 동일한 참조 값을 사용하는 함수가 된다.
useMemo를 이용한 결과값 최적화
- Memoization이란?
비용이 많이 드는 함수 호출의 결과를 저장하고 동일한 입력이 다시 발생할 때 캐시된 결과를 반환하여 컴퓨터 프로그램의 속도를 높이는데 주로 사용되는 최적화 기술.
- useMemo 적용하기
compute 함수에 넘겨주는 a, b의 값이 이전과 동일하다면 컴포넌트가 리렌더링 되더라도 연산을 다시 하지 않고 이전 렌더링 때 저장해둔 값을 재활용한다.
function Component({a, b}) {
const result = useMemo(() => compute(a, b), [a, b])
return <div>{result}</div>
}
localStorage에 데이터 담기
Window.localStorage - Web API | MDN