React Hook + API

PARK·2021년 1월 19일
0

REACT 파헤치기

목록 보기
2/7
post-thumbnail

useEffect

이번에는 useEffect 라는 Hook 을 사용하여 컴포넌트가 처음 나타났을 때, 사라질 때, 특정 props가 바뀔 때, 함수를 넣어서 작업을 수행하는 useEffect입니다.

 useEffect(()=> { 
 //componentDidMount, domponentDidUpdate 역할
    	return() => { // componentWillUnmount 역할
	}
 })

클래스형과 단순 비교하면 위와 같습니다.

useEffect를 이해하기 위해서는 clean up함수와 effect함수를 알고 넘어가야 좋습니다.

 useEffect(()=> { 
 		//여기가 Effect !!
   return() => { 
		//여기가 clean-up !!
    }
 })

Effect

이펙트 함수는 useEffect의 본 역할을 한다고 생각할 수 있습니다. 특정한 작업을 처리하는데요. 여기서 '어느 시점에 작업을 처리하는가?' 이 궁금증을 해결해야만 useEffect를 이해하겠죠.

질문에 대한 답은 렌더링 이후 발생한다 입니다.
이해를 돕기 위한 과정을 보면

렌더링 => 돔 업데이트 => 이펙트

컴포넌트가 렌더링을 하고 결과물을 리액트에게 알립니다. 이 과정에서 리액트는 '이펙트' 함수를 기억합니다. 그리고 브라우저를 통해 DOM을 업데이트 합니다. 그리고 나서 이펙트를 실행합니다.

개념적으로 이펙트는 렌더링 이후에 발생하는 렌더링의 결과물이라고 생각할 수 있습니다. (엄격하게는 아닙니다!!)

왜냐면 이펙트는 렌더링의 성격과 비슷하게 렌더링을 할 때 마다 새롭게 생성되는 함수이기 때문입니다.

Cleanup

useEffect에서는 함수를 반환 할 수 있는데 이를 cleanup 함수라고 부릅니다

function App() {
  //생략
  const [users, setUsers] = useState([
    {
      id: 1,
      username: 'velopert',
      email: 'public.velopert@gmail.com',
      active: true
    },
    {
      id: 2,
      username: 'tester',
      email: 'tester@example.com',
      active: false
    },
    {
      id: 3,
      username: 'liz',
      email: 'liz@example.com',
      active: false
    }
  ]);
  useEffect(() => {
    console.log('user 값이 설정됨');
    console.log(users);
    return () => {
      console.log('user 가 바뀌기 전..');
      console.log(users);
    };
  }, [users]);
  const onDelete = id => { 
    setUsers(users.filter(user => user.id !== id));
  };  
  return (
    <div className="App">
      {users.map(users =>
      <UserList 
      users = {users}
      key={users.id} 
      onDelete ={onDelete}      
      />)}   
    </div>
    );
}
export default  App;
return ( 
        <div>
            {<b style = {{
                cursor : 'pointer',
                color : users.active ? 'green' : 'black'
            }}
{users.username} : {users.email}
            </b> }
            <button onClick = {() => onDelete(users.id)}>삭제</button>
        </div>
    );

버튼을 누르면 해당 user를 삭제하는 코드입니다. 일부코드는 생략했습니다.그래도 이해하실겁니다. 여기서 userEffect를 보면 어떻게 실행될까요??

맨 처음에 마운트되면서

console.log('user 값이 설정됨');
console.log(users); //객체 3개가 출력!!

그리고 버튼을 누르면

console.log('user 가 바뀌기 전..');
console.log(users); //객체 3개가 출력!!

그리고 다시 이펙트가 실행됨으로

console.log('user 값이 설정됨');
console.log(users); //삭제된 객체 제외 2개가 출력!!

렌더링 => 돔 업데이트 => 클린업(최신 x) => 이펙트

클린업이 들어가면 위와 같이 순서가 바뀐다고 생각하면 됩니다. 여기서 중요한 것은 최신의 prop가 아닌 구 버전 렌더링에 있던 클린업을 읽는 것입니다. 그렇기 때문에 다음과 같은 작업을 수행할 때 사용합니다.

-setInterval, setTimeout 을 사용하여 등록한 작업들clear 하기 (clearInterval, clearTimeout)
-라이브러리 인스턴스 제거

클린업(현 버전) => 렌더링 => 돔 업데이트 => 이펙트

위 순서와는 명확히 다르다는 것을 알고, 그 증거로 출력되는 객체 수를 확인하시면 좋겠습니다.

useEffect를 설명하면 deps를 빼놓을 수 없습니다.

deps가 만약 없다면?

useEffect(() => {
    //
  }, );

위 코드는 effect, clean-up 둘 다 렌더링될 때 마다 실행됩니다.

deps에 값이 있다면?

useEffect(() => {
    //
  },[deps] );

deps가 없다면 매번 이펙트가 실행되는 문제를 위에서 확인했습니다. 이 문제를 해결하기 위해서 의존성 배열 deps를 사용합니다. 리액트는 의존성 배열의 변경이 있을 경우에만
이펙트를 실행 시킵니다.

즉, 리액트에게 deps 이외 다른 props는 쓰지 않는다고 약속하는 것과 같습니다. deps의 값이 업데이트되면 effect, 삭제된다면 effect, clean-up 둘 다 실행시킵니다.

deps가 비어있다면?

useEffect(() => {
    //
  },[] );

리액트는 의존성 배열의 변경이 있을 경우에만
이펙트를 실행 시키는데 [ ]는 리액트에게 아무것도 useEffect를 통해 변경하지 않겠다고 다짐하는 것과 같습니다.

[ ]라고 정의했기 때문에 effect를 절대 다시 실행하지 않고, 한 번 적용되어도 안전하다는 뜻이기도 합니다

그래서 컴포넌트가 처음 마운트될 때만 이펙트가 실행됩니다.

useRef

함수 컴포넌트에서 특정 DOM을 선택해야 할 때 사용합니다.
특정 DOM을 선택하는 용도 외에도, 다른 용도가 한가지 더 있습니다. 바로, 컴포넌트 안에서 조회 및 수정 할 수 있는 변수를 관리하는 것 입니다.
(프로퍼티인 .current를 통해서 값을 수정, 조회 합니다.)

useRef로 관리하는 변수는 값이 바뀐다고 해서 컴포넌트가 리렌더링되지 않습니다. useRef로 관리하고 있는 변수는 설정 후 바로 조회 할 수 있습니다.

useRef는 다음과 같은 값을 관리 할 수 있습니다.
-setTimeout, setInterval 을 통해서 만들어진 id
-외부 라이브러리를 사용하여 생성된 인스턴스
-scroll 위치

useMemo

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

첫 번째 인자로 함수를 받는다. useMemo 훅은 이 함수가 반환하는 값을 기억 (메모이제이션)
두 번째 인자로 의존성 배열을 받는다. 의존성 배열이 변경되지 않으면 이전에 반환된 값 사용

메모이제이션(memoization)

은 컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술이다.

useMemo는 의존성이 변경되었을 때에만 메모이제이션된 값만 다시 계산 할 것입니다. 의존성 배열이 없다면 렌더링마다 실행합니다. useMemo는 계산량이 많은 함수를 처리할 때 사용하기 좋습니다.

그리고 useMemo는 렌더링 중에 실행됩니다.

Expected an assignment or function call and instead saw an expression.

당연히 함수를 전달하던가 return을 해야만 합니다.
(위는 그렇지 않을 경우에 대해 설명입니다..)

useCallback

부모 컴포넌트가 렌더링되면 자식들도 리-렌더링됩니다.
리-렌더링은 모든 함수의 재생산을 의미합니다.
( 리액트팀은 렌더링마다 함수를 재생산하는 것이 브라우저 성능에 큰 문제를 안 준다고 설명합니다. )

여기서도 메모이제이션을 활용합니다.

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

의존성에 포함된 값이 변경되지 않는다면 이전에 생성된 함수 참조 값을 반환해줍니다(메모이제이션)
의존성이 포함된 값이 변경된다면 메모이제이션이 바뀌면서 콜백을 반환할겁니다.

의존성에 []을 보내면 컴포넌트는 처음 렌더링될 때 갖던 값을 이후에도 동일한 참조 값으로 사용합니다. 빈 배열은 함수 내에서 해당 값들을 참조할때 가장 최신 값을 참조 할 것이라고 보장 할 수 없음을 기억하세요

React.memo

shallow compare 통해 동일한 참조 값의 prop이 들어온다면 리렌더링을 방지시킵니다. 사용법은 간단합니다.

export default React.memo(컴포넌트명);
const 컴포넌트 명 = React.memo(function 컴포넌트 명(){ 
// 
}

shallow compare (얕은 비교)

간단히 말하자면 객체, 배열, 함수와 같은 참조 타입들을 실제 내부 값까지 비교하지 않고 동일 참조인지(동일한 메모리 값을 사용하는지)를 비교하는 것을 뜻한다.

state에 push, pop, shite.. 사용하면 안되는 이유이기도 합니다. 원본을 사용하면 같은 참조값을 나타내니까요

참고
https://velog.io/@yejinh/useCallback%EA%B3%BC-React.Memo%EC%9D%84-%ED%86%B5%ED%95%9C-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%B5%9C%EC%A0%81%ED%99%94

https://ko.wikipedia.org/wiki/%EB%A9%94%EB%AA%A8%EC%9D%B4%EC%A0%9C%EC%9D%B4%EC%85%98
https://react.vlpt.us/basic/19-React.memo.html

https://rinae.dev/posts/a-complete-guide-to-useeffect-ko

https://ko.reactjs.org/docs/hooks-faq.html

https://ko.reactjs.org/docs/hooks-effect.html

https://react.vlpt.us/basic/16-useEffect.html

profile
익숙한 것에 작별을 고해야한다

0개의 댓글