이 글은 react todo-list 프로젝트 기준으로 작성되었습니다.
forceUpdate()
함수가 실행될 때shouldComponentUpdate()
라이프사이클을 사용하여 리렌더링을 방지할 수 있다. 그러나 함수형 컴포넌트에서는 라이프사이클 훅을 사용할 수 없다. 때문에 함수형 컴포넌트에서는 React.memo 함수를 사용한다.import React, { useState, useCallback } from 'react';
import cn from 'classnames';
import './TodoItem.scss'
const TodoItem = ({ todo, onChangeTodo, onRemoveTodo }) => {
...
};
export default React.memo(TodoItem); // 컴포넌트를 React.memo로 감싸주면, 적용 완료
// todo, onChangeTodo, onRemoveTodo가 변경되지 않으면 리렌더링을 하지 않는다.
import React from 'react';
import TodoItem from './TodoItem';
const TodoList = ({ todos, onChangeTodo, onRemoveTodo }) => {
...
};
export default React.memo(TodoList);
useState()
의 함수형 업데이트 사용useReducer()
사용// 함수형 업데이트 예제
const [ number, setNumber ] = useState(0);
const onIncrease = useCallback(
() => setNumber(prevNumber => prevNumber + 1), // 이전값에 접근 가능
[]
);
/*
setNumber(number + 1)이 아니라 위 코트처럼 어떻게 number를 업데이트할지 정의해주는
업데이트 함수를 넣어주게 되면 useCallback을 사용할 때 두 번째 매개변수로 넣는 배열에
number를 넣지 않아도 된다.
*/
const onAddTodo = useCallback((text) => {
if(text.trim() === '') return;
const newTodo = {
id: nextId.current,
text: text,
isDone: false,
isImportant: false
}
// const nextTodos = [...todos, newTodo];
// setTodos(nextTodos);
setTodos(todos => [...todos, newTodo]);
nextId.current++;
}, []);
const onChangeTodo = useCallback((id, prop, value) => {
setTodos(todos =>
todos.map(todo => { // 불변성 때문에.... 이렇게 수정해야함..
return todo.id === id ? {...todo, [prop]: value} : todo;
}
)
);
}, []);
const onRemoveTodo = useCallback((id) => {
setTodos(todos => todos.filter(todo => todo.id !== id));
}, [])
function todoReducer(todos, action) { // reducer 함수
switch (action.type) {
case 'INSERT':
return todos.concat(action.todo);
case 'UPDATE':
return todos.map(todo =>
todo.id === action.id ? {...todo, [action.prop]: action.value} : todo
)
case 'REMOVE':
return todos.filter(todo => todo.id !== action.id);
default:
return todos;
}
}
const App = () => {
const [ todos, dispatch ] = useReducer(todoReducer, [
{ id: 1, text: '집가기', isDone: false, isImportant: false },
{ id: 2, text: '영화 보기', isDone: false, isImportant: false },
{ id: 3, text: '아무것도 안하기', isDone: true, isImportant: true },
]
);
const onAddTodo = useCallback((text) => {
if(text.trim() === '') return;
const newTodo = {
id: nextId.current,
text: text,
isDone: false,
isImportant: false
}
dispatch({type: 'INSERT', todo: newTodo});
nextId.current++;
}, []);
const onChangeTodo = useCallback((id, prop, value) => {
dispatch({type: 'UPDATE', id, prop, value});
}, []);
const onRemoveTodo = useCallback((id) => {
dispatch({type: 'REMOVE', id});
}, [])
}