1. useReducer
useReducer란 react hook들 중 하나로 컴포넌트 내부에 새로운 state를 생성하기 때문에 모든 useState는 useReducer로 대체가 가능하다. useReducer를 이용할 경우, useState와 달리 상태 관리 코드를 컴포넌트 외부로 분리할 수 있다.
1.1. useReducer 실기
6주차 프로젝트 업그레이드
useState | useReducer |
import './App.css'
import { useState, useRef } from 'react';
import Header from './components/Header';
import Editor from './components/Editor';
import List from './components/List';
const mockData = [
{
id: 0,
isDone: false,
content: "React 공부하기",
date: new Date().getTime(),
},
{
id: 1,
isDone: false,
content: "노래하기",
date: new Date().getTime(),
},
{
id: 2,
isDone: false,
content: "빨래하기",
date: new Date().getTime(),
},
];
function App() {
const [todos, setTodos] = useState(mockData);
const idRef = useRef(3);
const onCreate = (content) => {
const newTodo = {
id: idRef.current++,
isDone: false,
content: content,
date: new Date().getTime(),
}
setTodos([newTodo,...todos]);
};
const onUpdate = (targetId) => {
setTodos(todos.map((todo) =>
todo.id===targetId
? {...todo, isDone: !todo.isDone}
: todo
)
);
};
const onDelete = (targetId) => {
setTodos(todos.filter((todo) => todo.id !== targetId));
};
return (
<div className='App'>
<Header />
<Editor onCreate={onCreate} />
<List
todos={todos}
onUpdate={onUpdate}
onDelete={onDelete}
/>
</div>
);
}
export default App
|
import './App.css'
import { useState, useRef, useReducer } from 'react';
import Header from './components/Header';
import Editor from './components/Editor';
import List from './components/List';
const mockData = [
{
id: 0,
isDone: false,
content: "React 공부하기",
date: new Date().getTime(),
},
{
id: 1,
isDone: false,
content: "노래하기",
date: new Date().getTime(),
},
{
id: 2,
isDone: false,
content: "빨래하기",
date: new Date().getTime(),
},
];
function reducer(state, action) {
switch(action.type){
case 'CREATE':
return[action.data, ...state];
case 'UPDATE':
return state.map((item) =>
item.id === action.targetId
? {...item, isDone: !item.isDone}
: item
);
case 'DELETE':
return state.filter(
(item) => item.id !== action.targetId
);
default:
return state;
}
}
function App() {
const [todos, dispatch] = useReducer(reducer, mockData);
const idRef = useRef(3);
const onCreate = (content) => {
dispatch({
type: "CREATE",
data: {
id: idRef.current++,
isDone: false,
content: content,
date: new Date().getTime()
}
})
};
const onUpdate = (targetId) => {
dispatch({
type: "UPDATE",
targetId: targetId
})
};
const onDelete = (targetId) => {
dispatch({
type: "DELETE",
targetId: targetId
})
};
return (
<div className='App'>
<Header />
<Editor onCreate={onCreate} />
<List
todos={todos}
onUpdate={onUpdate}
onDelete={onDelete}
/>
</div>
);
}
export default App
|
2. 최적화
최적화란 웹 서비스의 선능을 개선하는 모든 행위를 일컫는다.
최적화 방법 | |
웹서비스 최적화 | React App 내부의 최적화 |
|
|
2.1. 연산 최적화 (useMemo)
useMemo란 "˚메모이제이션" 기법을 기반으로 불필요한 연산을 제거해 최적화 하는 리액트 훅이다.
자매품: useCallback
최적화 전 | 최적화 후 |
2.2. React.memo
React.memo란 컴포넌트를 인수로 받아 최적화된 컴포넌트로 만들어 반환하는 것을 뜻한다.
최적화 전 | 최적화 후 |
import './Header.css';
const Header = () => {
return (
<div className="Header">
<h3>오늘은 📅</h3>
<h1>{new Date().toDateString()}</h1>
</div>
);
}
export default Header;
|
import './Header.css';
import { memo } from 'react';
const Header = () => {
return (
<div className="Header">
<h3>오늘은 📅</h3>
<h1>{new Date().toDateString()}</h1>
</div>
);
}
const memoizedHeader = memo(Header)
export default memoizedHeader;
|
2.3. useCallback
useCallback 사용X | useCallback 사용O |
++ 추가 스터디: useMemo vs useCallback
메모이제이션: 기존에 연산한 결과값을 메모리에 저장해두고 동일한 입력이 들어오면 재활용하는 기법
useMemo | useCallback |
메모이제이션 된 '값'을 반환하는 함수 복잡한 계산의 결과를 재사용하여 불필요한 연산을 피한다. |
메모이제이션 된 '함수'를 반환하는 함수 컴포넌트가 리렌더링될 때 동일한 함수 객체를 재사용하여 불필요한 재생성을 피한다. |
|
|
import { useState, useMemo } from "react";
export default function App() {
const [ex1, setEx1] = useState(0);
const [ex2, setEx2] = useState(0);
const handleEx1 = () => {
setEx1((prev) => prev + 1)
};
const handleEx2 = () => {
setEx2((prev) => prev + 1)
};
useMemo(() => {console.log(ex1)}, [ex1]);
return (
<>
<button onClick={handleEx1}>X</button>
<button onClick={handleEx2}>Y</button>
</>
);
}
|
import { useState, useCallback } from "react";
function App() {
const [ex1, setEx1] = useState(0);
const [ex2, setEx2] = useState(0);
const handleEx1 = () => {
setEx1((prev) => prev + 1)
};
const handleEx2 = () => {
setEx2((prev) => prev + 1)
};
const returnUseCallback = useCallback(
() => {console.log(ex2)}, [ex1]);
returnUseCallback();
return (
<>
<button onClick={handleEx1}>X</button>
<button onClick={handleEx2}>Y</button>
</>
);
}
export default App;
|
X버튼을 누를 때 콘솔 창에 ex1 값이 출력된다. Y버튼의 경우, 누르더라도 App 컴포넌트가 전부 리렌더링 되지만 ex1의 값은 변하지 않기 때문에 useMemo에는 아무런 변화가 나타나지 않는다. |
Y버튼을 다섯 번 눌렀을 경우, Y버튼이 눌리면 ex2라는 상태 값이 1씩 증가한다. 그러나, 출력 값은 계속 0이다. 이 상태에서 X버튼을 누르면 ex1의 deps가 변하게 되면서 () => {console.log(ex2)를 반환하게 되어 ex2=5일 때의 값을 반환한다. (출력값이 변하지 않았더라도 실제 메모리에서는 ex2의 값이 1씩 증가하고 있었기 때문) 즉, deps가 변해야 함수 컴포넌트와 상태 값(ex2)이 공유된다. |
3. Context
ReactJS에서 Context란 컴포넌트 간의 데이터를 전달하는 또 다른 방법으로 기존의 props가 가지고 있던 단점인 props drilling을 해결할 수 있다.
props의 단점: props는 부모 --> 자식 으로만 데이터를 전달할 수 있었음. 즉 계층구조상, 위 컴포넌트에서 두 단계 이상의 컴포넌트에게 다이렉트로 데이터를 전달할 수 없음.
3.1. Context 실기
컴포넌트 안 쪽에서 context 객체를 생성하게 되면 문제가 발생하지는 않으나 컴포넌트가 리렌더링 될 때마다 계속 새로운 context를 생성하게 되어 보통 context는 컴포넌트 외부에 선언을 하게 된다.
Context 생성 |
프로젝트 파일
'P&P 학회 > FRONT-END STUDY' 카테고리의 다른 글
8주차 (2024.11.25) (1) | 2024.11.25 |
---|---|
6주차 (2024.11.11) (1) | 2024.11.08 |
5주차 (2024.11.04) (0) | 2024.10.30 |
4주차 (2024.10.28) (0) | 2024.10.18 |
3주차 (2024.10.14) (0) | 2024.10.09 |