[Chapter 4] To-Do 앱 최적화 하기 - 4

서희찬·2022년 3월 4일
0

The Origin : React

목록 보기
9/17
post-thumbnail

리액트 불변성 지키기

불변성

원시 탑입은 불변성을 가지고 있고 참조타입은 가지고 있지않다.

  • 원시타입 : Boolean, String ... (불변성O)
  • 참조타입 : Object, Array (주소값을 저장한다)


    결론 : 원시는 실제 변경 X, 참조는 실제 변경 O

불변성을 지켜야 하는 이유는 ?

  1. 참조 타입에서 객체난 배열의 값이 변할 때 원본 데이터가 변경되기에 이 원본 데이터를 참조하고 있는 다른 객체에서 예상치 못한 오류가 발생할 수 있어서 프로그래밍 복잡도가 올라간다.
  2. 리액트에서 화면을 업데이트할 때 불변성을 지켜서 값을 이전 값과 비교해서 변경된 사항을 확인한 후 업데이트하기 때문에 불변성을 지켜줘야 한다.

불변성을 지키는 방법은?

참조 타입에서는 값을 바궜을 때 Call Stack 주소 값은 같은데 Heap 메모리 값만 바뀌기에 불변성 유지 불가하므로 아예 새로운 배열을 반환하는 메소드 사용

  • spread operator, map, filter, slice, reduce ...
    원본 데이터 변경 메소드 => splice, push !

List 컴포넌트 생성하기

List안에 각 리스트들을 컴포넌트로 생성하기 위해
Lists.js라는 파일을 새로만든다.
원래 s빼야하는데 기존에 있던걸 List라고해서 Lists로 진행하자 !
그러고 list div들을 빼고 필요 함수들을 Lists.js로 옮겨주고
List.js에서는 < Lists /> 해서 컴포넌트를 불러와주면 끝 !

그러고 필요한 값들을 List.js에서 전달해주자

Lists.js

//rfce
import React from 'react'



const Lists = (
    {id,title,completed,todoData,setTodoData,provided,snapshot}
) =>{
    const handleCompleteChange = (id) =>{
        let newTodoData = todoData.map(data=>{
        if(data.id === id){
        data.completed = !data.completed; 
        }
        return data; 
    });
    setTodoData(newTodoData);
    this.setState({todoData:newTodoData});
    };


    //할일 목록 삭제 함수 
    const hanndleClick=(id)=>{
        //filter method를 사용해서 
        //id가 같은거를 필터링 해버리자 
        let newTodoData = todoData.filter(data=> data.id !== id);
        console.log('newTodoData',newTodoData);
        //list의 id가 와서 데이터의 아이디가 아닌것만 트루를 반환해서 살린다 
        setTodoData(newTodoData);
    };

  return (
    <div 
    key={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-2 my-2 text-gray-600  border rounded`}
    >
            <div className="items-center" >
                <input type="checkbox" defaultChecked={completed} onChange={()=>handleCompleteChange(id)}></input>
                <span className={completed ? "line-through" :undefined}>{title}</span>
            </div>
            <div className="items-center">
                <button className="px-4 py-2 float-right" onClick={()=>hanndleClick(id)}>X</button>
            </div>
        </div>
  )
}

export default Lists

List.js

// rfc 엔터하면 함수형 컴포넌트 만들기 가능 
import React from 'react'
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
import Lists from './Lists';

export default function List({todoData, setTodoData}) {



const handleEnd = (result) =>{
    //result 매개변수에는 source 항목 및 대상 위치와 같은 드래그 이벤트에 대한 정보가 포함됩니다.
    console.log(result);

    // 목적지가 없으면 함수 종료
    if(!result.destination) return;

    // 리액트 불변성 지키기 위한 새로운 tododata생성 
    const newTodoData = todoData; 

    // 1. 변경시키는 아이템을 배열에서 지움 
    // 2. return 값으로 지워진 아이템을 잡음 
    const [reorderItem] = newTodoData.splice(result.source.index, 1);

    // 원하는 자리에 reoderedItem을 invert 한다
    newTodoData.splice(result.destination.index, 0, reorderItem);
    setTodoData(newTodoData);
};

  return (  
    <div>
        {/* // this는 클래스를 가리키고 클래스 안에 todoData라는 리스트객체를 가지고 와서 그 안에 데이터를 꺼내는데 map함수를 써서 꺼낸다
        // map은 객체별 요소를 data라는 변수로 정해주고 data객체 안에 id,completed,title을 가져온다
        // style같은경우도 겹치는 경우가 많으니 this를 사용해서 클래스 내에 만들어둔 스타일을 가지고 와서 사용한다
        // react에서는 반복되는 값들을 가지고올때 유니크한 값와 같은 key값을 줘야한다  */}
        <DragDropContext onDragEnd={handleEnd}>
            <Droppable droppableId="todo">
                {(provided)=> (
                    <div {...provided.droppableProps} ref={provided.innerRef}>
                        {todoData.map((data,index)=>(
                            <Draggable
                            key={data.id}
                            draableId={data.id.toString()}
                            index={index}
                            >
                                {(provided,snapshot)=>(
                                    <Lists 
                                    key={data.id}
                                    id={data.id}
                                    title={data.title}
                                    completed={data.completed}
                                    todoData={todoData}
                                    setTodoData={setTodoData}
                                    provided={provided}
                                    snapshot={snapshot}
                                    />
                                )}
                            </Draggable>
                        ))}
                        {provided.placeholder}
                    </div>
                )}
            </Droppable>
        </DragDropContext>
    </div>
  )
}


이렇게 된다..!

React.memo를 이용한 렌더링 최적화

현재 Todo 앱의 문제점

현재 투두앱은 컴포넌트가 나눠져있다
이렇게 나눠진 이유는 재사용성을 위해서이기도 하지만 각 컴포넌트의 렌더링 최적화를 위해서이다.
예를들어 Form에서 글을 타이핑 할 때 원래는 Form컴포넌트와 그 State값을 가지고 있는 App 컴포넌트만 렌더링 돼야하는데 현재는....

다 렌더링되고 있는 문제점이 있다..

이 문제점을 React.memo를 통해 해결하자

React.memo를 감싸주고,
이렇게 Lists.js,List.js를 모두 감싸주면된다.

useCallback을 이용한 함수 최적화


함수를 만들때 최상위 컴포넌트에 자주 만든다!

React.useCallback 적용으로 문제 해결

handleClick함수를 제일 최상위 컴포넌트 App.js로 옮겨주자
그러면 App.js->List.js->Lists.js로 하나씩 전달해줘야지
Lists.js가 handleClick함수를 App.js에서 가져올 수 있다.

그래서handleClick함수를 useCallback으로 감싸주면

    const hanndleClick=useCallback((id)=>{
      //filter method를 사용해서 
      //id가 같은거를 필터링 해버리자 
      let newTodoData = todoData.filter(data=> data.id !== id);
      console.log('newTodoData',newTodoData);
      //list의 id가 와서 데이터의 아이디가 아닌것만 트루를 반환해서 살린다 
      setTodoData(newTodoData);
  },
  [todoData] //todoData가 변할때만 다시생성할 수 있게한다! 
  );

이렇게 감싸주고 두번째 인수로 todoData를 전달하여 데이터가 변할때마다만 다시 생성 할 수 있게 만들어 준다.

useMemo를 이용한 결과 값 최적화

Memoization 이란?

메모이제이션은 비용이 많이 드는 함수 호출의 결과를 저장하고 동일한 입력이 다시 발생할 때 캐시된 결과를 반환하여 컴퓨터 프로그램의 속도를 높이는데 주로 사용되는 최적화 기술

useMemo적용하기

useMemo로 감싸준 후에 첫번째 인수에 의존성 배열에 compute 함수에서 사용하는 값을 넣어준다.

리액트 확장 프로그램 추가하기

이런걸 추가하면 리액트 검사할때 좋당

profile
Carnegie Mellon University Robotics Institute | Research Associate | Developing For Our Lives, 세상에 기여하는 삶을 살고자 개발하고 있습니다

0개의 댓글

관련 채용 정보