[벨로퍼트님 개념] 1장(4) 리액트

초록귤·2022년 1월 14일
1
post-thumbnail

19. React.memo를 사용한 컴포넌트 리렌더링 방지

  • 컴포넌트의 porps가 바뀌지 않았다면, 리렌더링을 방지하여 컴포넌트의 리렌더링 성능 최적화를 해줄 수 있는 React.momo 함수

  • 컴포넌트에서 리렌더링이 필요한 상황에서만 리렌더링을 하도록 설정해줄 수 있다.

  • 사용법 : 감싸주기만 하면 된다!.

CreateUser.js

import React from 'react';

const CreateUser = ({ username, email, onChange, onCreate }) => {
  return (
    <div>
      <input
        name="username"
        placeholder="계정명"
        onChange={onChange}
        value={username}
      />
      <input
        name="email"
        placeholder="이메일"
        onChange={onChange}
        value={email}
      />
      <button onClick={onCreate}>등록</button>
    </div>
  );
};

export default React.memo(CreateUser);

UserList.js

import React from 'react';

const User = React.memo(function User({ user, onRemove, onToggle }) {
  return (
    <div>
      <b
        style={{
          cursor: 'pointer',
          color: user.active ? 'green' : 'black'
        }}
        onClick={() => onToggle(user.id)}
      >
        {user.username}
      </b>
      &nbsp;
      <span>({user.email})</span>
      <button onClick={() => onRemove(user.id)}>삭제</button>
    </div>
  );
});

function UserList({ users, onRemove, onToggle }) {
  return (
    <div>
      {users.map(user => (
        <User
          user={user}
          key={user.id}
          onRemove={onRemove}
          onToggle={onToggle}
        />
      ))}
    </div>
  );
}

export default React.memo(UserList);

적용을 다 하고나서, Input을 수정할 때 하나라도 수정하면 모든 User들이 리렌더링되고, CreateUser도 리렌더링이 된다.

users배열이 바뀔때마다 onCreate도 새로 만들어지고, onToggle, onRemove도 새로 만들어지기 때문이다.
deps에 users가 들어있기 때문에 배열이 바뀔때마다 함수가 새로 만들어지는건 당연하다.

최적화하고 싶다면?

바로 deps에서 users를 지우고, 함수들에서 현재 useState로 관리하는 users를 참조하지 않게 하는것이다.
정답은 바로, 함수형 업데이트

함수형 업데이트를 하게 되면, setUsers에 등록하는 콜백함수의 파라미터에서 최신 users를 참조할 수 있기 때문에 deps에 users를 넣지 않아도 된다.
=> 이렇게 해주면, 특정 항목을 수정하게 될 때, 해당 항목만 리렌더링 될 거다.

  • 리액트 개발을 할 때, useCallback, useMemo, React.memo 는 컴포넌트의 성능을 실제로 개선할 수 있는 상황에서만 해야한다.
  • ex) User 컴포넌트에 b와 button에 onClick으로 설정해준 함수들은, 해당 함수들을 useCallback으로 재사용한다고 해서 리렌더링을 막을 수 있는 것은 아니므로, 굳이 그렇게 할 필요가 없다.
  • 추가적으로, 렌더링 최적화하지 않을 컴포넌트에 React.memo를 사용하는 것은, 불필요한 props 비교만 하는 것이기 때문에 실제로 렌더링을 방지할 수 있는 상황이 있는 경우에만 사용해야한다.(권장)
  • 추가적으로, React.memo 에서 두번째 파라미터에 propsAreEqual 이라는 함수를 사용하여 특정 값들만 비교를 하는 것도 가능합니다.
export default React.memo(
  UserList,
  (prevProps, nextProps) => prevProps.users === nextProps.users
);

하지만, 이걸 잘못사용한다면 오히려 의도치 않은 버그들이 발생하기 쉽다.
ex) 함수형 업데이트로 전환을 안했는데 이렇게 users만 비교를 하게 된다면, onToggle과 onRemove에서 최신 users배열을 참조하지 않으므로 심각한 오류가 발생할 수 있다.

20. useReducer를 사용하여 상태 업데이트 로직 분리

useReducer 이해하기

  • 우리가 이전에 만든 사용자 리스트 기능에서의 주요 상태 업데이트 로직은 App 컴포넌트 내부에서 이루어졌었다.
  • 상태를 업데이트할 때에는 useState를 사용해서 새로운 상태를 설정해주었는데, 상태를 관리하게 될 때 useState를 사용하는 것 말고도 다른 방법이 있다.
  • 바로, useReducer를 사용하는 것
    • 이 Hook 함수를 사용하면 컴포넌트의 상태 업데이트 로직을 컴포넌트에서 분리시킬 수 있다.
    • 상태 업데이트 로직을 컴포넌트 바깥에 작성할 수도 있고, 심지어 다른 파일에 작성 후 불러와서 사용할 수 있다.
    • reducer는 현재 상태와 액션 객체를 파라미터로 받아와서 새로운 상태를 반환해주는 함수 = 곧 컴포넌트가 지닐 새로운 상태
    • 여기서 action은 업데이트를 위한 정보를 가지고 있다. 주로 type값을 지닌 객체 형태로 사용하지만 꼭 따라야 할 규칙은 없다.
function reducer(state, action) {
  // 새로운 상태를 만드는 로직
  // const nextState = ...
  return nextState;
}


액션의 예시 . 

// 카운터에 1을 더하는 액션
{
  type: 'INCREMENT'
}
// 카운터에 1을 빼는 액션
{
  type: 'DECREMENT'
}
// input 값을 바꾸는 액션
{
  type: 'CHANGE_INPUT',
  key: 'email',
  value: 'tester@react.com'
}
// 새 할 일을 등록하는 액션
{
  type: 'ADD_TODO',
  todo: {
    id: 1,
    text: 'useReducer 배우기',
    done: false,
  }
}

이처럼 action 객체의 형태는 자유다.
type 값을 대문자와 _로 구성하는 관습이 존재하기도 하지만, 꼭 따라야 할 필요는 없다.

useReducer의 사용법

const [state, dispatch] = 
useReducer(reducer, initialState);

useReducer vs useState 뭘 쓸까?

예를 들어서 컴포넌트에서 관리하는 값이 딱 하나고, 그 값이 단순한 숫자, 문자열 또는 boolean 값이라면 확실히 useState 로 관리하는게 편할 것입니다.

const [value, setValue] = useState(true);
하지만, 만약에 컴포넌트에서 관리하는 값이 여러개가 되어서 상태의 구조가 복잡해진다면 useReducer로 관리하는 것이 편해질 수도 있습니다.

const [<상태 객체>, <dispatch 함수>] = useReducer(<reducer 함수>, <초기 상태>, <초기 함수>)

=> 벨로퍼트님 같은 경우네는 setter를 한 함수에서 여러번 사용해야 하는 일이 발생한다면, 이때부터 useReducer를 쓸까 고민을 한다고 한다.

  • useReducer를 썼을때 편해질 것같으면 useReducer를 쓰고,

setUsers(users=> users.concat(user));
setInputs({
username: '',
email:''
});

input의 키값 [name]
input[name] = value 로 키 값을 교체해준다고 이해하면 된다.


name,
value, 의 표현과 같다.

profile
초록색 귤이 노랑색으로 익어가듯, 실력이 익어가기 위해 노력하는 개발자 lahee입니다. 프론트엔드 개발자를 목표로 성장하고 있습니다.

1개의 댓글

comment-user-thumbnail
2022년 1월 16일

좋은 글 감사합니다! >_o

답글 달기