React - velopert html 정리(2)

김정욱·2020년 10월 2일
0

React

목록 보기
6/22
post-thumbnail

(본 글은 Velopert님의 https://react.vlpt.us/ 를 정리한 내용입니다.)

useEffect와 lifeCycle

useEffect(<function>, <Array>);

: class형 컴포넌트에서 componentDidMount, componentDidUpdate와 같은
  lifeCyclehooks에서는 useEffect로 구현한다.

  • useEffect은 다음과 같은 두개의 인자를 받는다.
  • Array의 여부어떤 값이 들어가냐에 따라 기능이 달라진다.
    (기본적으로 useEffectcomponentDidMount 될 때 실행된다.)

  • 컴포넌트가 마운트 되었을 때 / re-render 될 때 마다 실행
useEffect(() => {
    console.log("componentDidMount & re-render !");
  });

  • 컴포넌트가 마운트 되었을 때
useEffect(() => {
    console.log("componentDidMount");
  }, []);

: Array에 []가 들어간 것에 의미어떠한 state가 변화되더라도 re-render하지 않겠다는 의미!
  즉, 컴포넌트가 최초 마운트 될 때만 실행하게 된다!


  • 컴포넌트가 마운트 되었을 때 / 특정 state값이 변화 할 때마다 실행
useEffect(() => {
    console.log("componentDidMount & state change !");
  }, [number]);

: number이라는 state값이 변할 때 마다 실행되게 된다.


  • 컴포넌트가 삭제될 때 --> return()
useEffect(() => {
    console.log("componentDidMount!");
    return (
       console.log("componentDidUnmount !")
    )
  }, []);

: return을 지정하면 컴포넌트가 삭제될 때 실행된다.
(lifecycle의 componentWillUnmount 역할)

최적화

  • useMemo()
    : re-render로 함수의 결과 값을 다시 얻기 위해 불필요하게 재 실행되는 경우를 막기 위한 방법
       결과 값을 기억하는 용도로 사용한다.
       (반드시 useMemo내부에서 사용되는 state두번째 인자 배열에 넣어줘야 한다!)


  function countActiveUsers(users) {
    console.log('활성 사용자 수를 세는중...');
    return users.filter(user => user.active).length;
  }

function App() {

  ...
  // const count = countActiveUsers(users);
  const count = useMemo(() => countActiveUsers(users), [users]);
  ...

}

: 함수형 컴포넌트 외부에 선언되어 있어서 re-render시countActiveUsers()가
   재 선언되지는 않지만
, 내부에서 사용하는 구문이 재 실행되어 불필요하게 재 실행되게 된다.
   따라서 useMemo()를 이용해 특정 값이 바뀔 때만 실행되게 할 수 있다.


  • useCallback()
    : useMemo()가 함수의 결과 값을 기억했다면, useCallback()은 함수의 불필요한 선언을
      막기 위해 함수 자체를 기억하여 최적화를 이뤄내는 기능이다.


    [ 미 적용 ]

const onCreate = () => {
  setUsers(users.concat(user));
  setInputs({
    username: '',
    email: ''
  });
  nextId.current += 1;
};

const onRemove = id => {
  setUsers(users.filter(user => user.id !== id));
};
const onToggle = id => {
  setUsers(
    users.map(user =>
      user.id === id ? { ...user, active: !user.active } : user
    )
  );
};

: 다음과 같은 코드처럼 우리는 특정 이벤트를 처리하는 많이 정의한다. 하지만, 이러한 함수들은
  re-render될 때 재선언되어 불필요성을 가지게 된다. 따라서 useCallback()을 통해 내부에서
  사용하는 특정 state가 바뀔 때 만 재선언
되게 하여 효율적인 코드로 바꿀 수 있다.


[ 적용 ]

  // 내부에서 사용하는 users, username, email state값을 두번째 인자에 추가! 
  const onCreate = useCallback(() => {
    setUsers(users.concat(user));
    setInputs({
      username: '',
      email: ''
    });
    nextId.current += 1;
  }, [users, username, email]);

  // 내부에서 사용하는 users라는 state를 두번째 인자에 추가! 
  const onRemove = useCallback(
    id => {
      // user.id 가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듬
      // = user.id 가 id 인 것을 제거함
      setUsers(users.filter(user => user.id !== id));
    },
    [users]
  );

  // 내부에서 사용하는 users라는 state를 두번째 인자에 추가! 
  const onToggle = useCallback(
    id => {
      setUsers(
        users.map(user =>
          user.id === id ? { ...user, active: !user.active } : user
        )
      );
    },
    [users]
  );

  • React.memo()
    : 함수형 컴포넌트가 re-render될 때 렌더링 결과를 기억해서 다음 렌더링 시 props가 같으면 기억된 내용을 재사용 하여 불필요한 re-render를 줄일 수 있는 방법.
    (컴포넌트를 감싸는 래핑 방법이며, 고차 컴포넌트(hoc)라고 부른다.)
function CreateUser() {
  ...
}
export default React.memo(CreateUser);
  // 컴포넌트를 전달해 다시 컴포넌트를 반환하는 고차 컴포넌트 hoc!

혹은

function React.memo(CreateUser() {
  ...
})
export default CreateUser;

최적화 주의사항

  • useMemo() / useCallback() / React.memo()를 사용해 최적화를 할 때에 반드시 성능 개선이 필요 한 경우에만 하는 것이 좋다.
  • 복잡한 연산과 같이 꼭 필요한 경우 아니면 사용하지 않는 것이 좋다!
  • 조금만 실수가 발생해도 로직이 매우 복잡해져 이해하기 힘든 상황이 올 수 있다.

useReducer()

: 지금까지는 state와 관련 로직useState를 사용해서 컴포넌트 내부에 처리하였다.
  useReducer()를 이용해서 상태 업데이트 로직을 분리하여 컴포넌트 외부에서 상태관리 가능


[ 기존 Counter ]

import React, { useState } from 'react';

function Counter() {
  const [number, setNumber] = useState(0);

  const onIncrease = () => {
    setNumber(prevNumber => prevNumber + 1);
  };

  const onDecrease = () => {
    setNumber(prevNumber => prevNumber - 1);
  };

  return (
    <div>
      <h1>{number}</h1>
      <button onClick={onIncrease}>+1</button>
      <button onClick={onDecrease}>-1</button>
    </div>
  );
}

export default Counter;

[ useReducer()사용한 Counter ]

import React, { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      return state;
  }
}

function Counter() {
  const [number, dispatch] = useReducer(reducer, 0);

  const onIncrease = () => {
    dispatch({ type: 'INCREMENT' });
  };

  const onDecrease = () => {
    dispatch({ type: 'DECREMENT' });
  };

  return (
    <div>
      <h1>{number}</h1>
      <button onClick={onIncrease}>+1</button>
      <button onClick={onDecrease}>-1</button>
    </div>
  );
}

export default Counter;

: 컴포넌트 내부에서 처리해야하는 상태 관리 로직을 reducer로 분리할 수 있다.
해당 내용을 이해하려면 기본적인 Redux의 flow를 알 필요가 있다.
   ( 1. 컴포넌트 내부에서 dispatch()를 통해 action을 reducer에게 넘긴다.
      2. reducer는 상태 값을 변경 시킨다 )


  • Redux flow
    1) Component는 dispatch()를 통해 Action을 실행시킨다.
    2) Action의 반환 값을 통해 Reducer는 상태를 변경시킨다.
    3) Store에 상태가 저장된다.
    4) Component에서는 Store에 접근하여 상태 정보를 받는다.

  • Component -> action -> reducer 순서로 상태정보 변경
  • axios()와 같은 로직은 action 파일에 넣는다.
  • reducer에서는 해당 action에 대한 state 변경 부분만 넣는다.

[참고]
1. useReducer()로 Redux와 비슷한 역할을 하게 할 수 있는 것 같다.
2. 쌩 Redux와 react-redux를 쓰는 방법 이렇게 두가지가 있는 것 같다.

profile
Developer & PhotoGrapher

0개의 댓글