-
[React TodoList] 많은 데이터 렌더링, React.memo, 성능 최적화React/일정관리 프로젝트 2023. 3. 14. 18:34
React TodoList 보완하기
많은 데이터 렌더링 생성하기
import React , {useCallback, useState,useRef} from "react"; import TodoTemplate from "./TodoTemplate"; import TodoInsert from "./TodoInsert"; import TodoList from "./TodoList"; function createBulkTodos() { const array = []; for(let i=1;i<2500;i++){ array.push({ id:i, text : `할 일 : ${i}`, checked:false }) } return array } const App = () => { const [todos,setTodos] = useState(createBulkTodos) const nextId = useRef(2500); const onInsert = useCallback( text => { const todo = { id : nextId.current, text, checked :false, }; setTodos(todos.concat(todo)) nextId.current +=1; },[todos], ) const onRemove = useCallback( id => { setTodos(todos.filter(todo=>todo.id !== id)) },[todos] ) const onToggle = useCallback( id => { setTodos( todos.map(todo => todo.id === id ? {...todo, checked:!todo.checked}:todo,) ) },[todos] ) return ( <TodoTemplate> <TodoInsert onInsert={onInsert}/> <TodoList todos={todos} onRemove={onRemove} onToggle={onToggle} /> </TodoTemplate> ) } export default App;
성능 모니터링 (개발자 도구 - performance - 녹화)
1.4초로 성능이 매우 나쁘다는 것을 알 수 있다.
성능이 느린 이유
'할 일 : 1 ' 항목 체크시 app.state가 변경 => app.js 리렌더링 => todoList 등 자식 컴포넌트 렌더링
리렌더링 발생 원인
1. 자신이 전달받은 props가 변경될 때
2. 자신의 state가 변경될 때
3. 부모 컴포넌트가 리렌더링 될 때
4. forceUpdate 함수가 실행될 때React.memo를 사용해 컴포넌트 성능 최적화
리렌더링을 방지하기 위해 React.memo를 사용하여 props가 바뀌지 않으면 렌더링하지 않도록 설정한다.
todoListItem.js
import React from 'react'; (...) export default React.memo(TodoListItem);
onToggle, onRemove 함수가 바뀌지 않게 하기
현재 프로젝트는 todos 배열이 업데이트 되면 onRemove와 onToggle 함수도 새로 바뀐다.
컴포넌트 최적화를 위해 useState 또는 useReducer를 사용한다.
1. useState의 함수형 업데이트
setTodos 함수를 사용할 때 새로운 상태를 파라미터로 넣어 준다.
app.js
import React , {useCallback, useState,useRef} from "react"; import TodoTemplate from "./TodoTemplate"; import TodoInsert from "./TodoInsert"; import TodoList from "./TodoList"; function createBulkTodos() { const array = []; for(let i=1;i<2500;i++){ array.push({ id:i, text : `할 일 : ${i}`, checked:false }) } return array } const App = () => { const [todos,setTodos] = useState(createBulkTodos) const nextId = useRef(2500); const onInsert = useCallback( text => { const todo = { id : nextId.current, text, checked :false, }; setTodos(todos=> todos.concat(todo)) nextId.current +=1; },[], ) const onRemove = useCallback( id => { setTodos(todos=> todos.filter(todo=>todo.id !== id)) },[] ) const onToggle = useCallback( id => { setTodos( todos=> todos.map(todo => todo.id === id ? {...todo, checked:!todo.checked}:todo,) ) },[] ) return ( <TodoTemplate> <TodoInsert onInsert={onInsert}/> <TodoList todos={todos} onRemove={onRemove} onToggle={onToggle} /> </TodoTemplate> ) } export default App;
500ms로 줄은 걸로 보아 성능이 향상된 것을 알 수 있다.
2. useReducer 사용하기
두 번째 파라미터에 초기 상태를 넣어주어야 하지만 undefined를 설정하고 세 번째 파라미터에 초기 상태 생성하는 함수를 넣어 컴포넌트가 맨 처음 렌더링 될 때 1회 함수를 호출한다.
import React , {useCallback, useState,useRef, useReducer} from "react"; import TodoTemplate from "./TodoTemplate"; import TodoInsert from "./TodoInsert"; import TodoList from "./TodoList"; function createBulkTodos() { const array = []; for(let i=1;i<2500;i++){ array.push({ id:i, text : `할 일 : ${i}`, checked:false }) } return array } function todoReducer(todos,action) { switch(action.type) { case 'Insert' : return todos.concat(action.todo); case 'Delete' : return todos.filter(todo => todo.id !== action.id ); case 'Toggle' : return todos.map(todo => todo.id === action.id ? {...todo,checked:!todo.checked} : todo,) default: return todos; } } const App = () => { // const [todos,setTodos] = useState(createBulkTodos) const [todos,dispatch] = useReducer(todoReducer, undefined, createBulkTodos) const nextId = useRef(2500); const onInsert = useCallback( text => { const todo = { id : nextId.current, text, checked :false, }; dispatch({type:'Insert',todo}) // setTodos(todos=> todos.concat(todo)) nextId.current +=1; },[], ) const onRemove = useCallback( id => { // setTodos(todos=> todos.filter(todo=>todo.id !== id)) dispatch({type:'Delete',id}) },[] ) const onToggle = useCallback( id => { dispatch({type:'Toggle',id}) // setTodos( // todos=> todos.map(todo => // todo.id === id ? {...todo, checked:!todo.checked}:todo,) // ) },[] ) return ( <TodoTemplate> <TodoInsert onInsert={onInsert}/> <TodoList todos={todos} onRemove={onRemove} onToggle={onToggle} /> </TodoTemplate> ) } export default App;
useState와 useReducer 둘 중 어느 것을 사용해도 되고 성능은 비슷하다.
useReducer를 사용하면 상태를 업데이트 하는 로직을 컴포넌트 바깥에 둘 수 있다는 장점이 있다.
'React > 일정관리 프로젝트' 카테고리의 다른 글
[React TodoList] 리액트 투드리스트 마무리 - 깃허브, 파일 정리 (0) 2023.03.15 [React TodoList] 불변성, 최적화, 렌더링 최적화 (0) 2023.03.15 [React TodoList] 컴포넌트 기능 구현하기 - 체크 (0) 2023.03.14 [React TodoList] 컴포넌트 기능 구현하기 - 삭제 (0) 2023.03.14 [React TodoList] 컴포넌트 기능 구현하기 - 추가 (0) 2023.03.14