Skip to content

Latest commit

 

History

History
230 lines (220 loc) · 8.01 KB

2022_07_10.md

File metadata and controls

230 lines (220 loc) · 8.01 KB

useEffect : 컴포넌트가 마운트 (처음 나타났을 때), 언마운트 (사라질 때), 업데이트 (특정 props가 바뀔 때) 특정 작업을 처리

  • 컴포넌트의 내용이 업데이트되면 기존의 값을 언마운트하고 새로운 값을 마운트함
  • 첫 번째 파라미터에는 함수, 두 번째 파라미터에는 의존값이 들어있는 배열 (deps) 넣기
  • useEffect 안에서 사용하는 상태나 props 가 있다면, useEffect 의 deps 에 넣어주기 (useEffect가 최신 props / 상태를 가리키기 위함)
  • useEffect 에서 함수를 return할 수 있음 -> cleanup 함수 (뒷정리)

1. deps 가 빈배열

useEffect(() => { // 마운트
    console.log('컴포넌트가 화면에 나타남');
    return () => { // 언마운트
      console.log('컴포넌트가 화면에서 사라짐');
    };
  }, []);
  • 컴포넌트가 처음 마운트될 때 useEffect 내 함수 호출 (componentDidmount)
  • 컴포넌트가 언마운트될 때 cleanup 함수 호출 (componentWillUnmount)

2. deps 에 의존 값 있음

useEffect(() => { // 마운트
    console.log('user 값이 설정됨');
    console.log(user);
    return () => { // 언마운트
      console.log('user 가 바뀌기 전..');
      console.log(user);
    };
  }, [user]);
  • 컴포넌트가 처음 마운트될 때 useEffect 내 함수 호출 (componentDidmount)
  • 의존 값이 업데이트 됐을 때 (componentDidUpdate)
  • 컴포넌트가 언마운트될 때 cleanup 함수 호출 (componentWillUnmount)

3. deps 파라미터 생략

useEffect(() => { // 마운트
    console.log(user);
  });
  • 그냥 컴포넌트가 리렌더링될 때마다 함수 호출됨

  • 리액트 컴포넌트는 기본적으로 부모 컴포넌트가 리렌더링되면 자식 컴포넌트 또한 리렌더링됨
  • Virtual DOM 에 모두 렌더링 -> 바뀐 내용이 있는 컴포넌트만 실제 DOM 에 반영

useMemo : 성능 최적화할 때 사용

  • "memorized" 이전에 계산한 값을 재사용한다는 의미
  • 첫 번째 파라미터에는 어떻게 연산할지 정의하는 함수를, 두 번째 파라미터에는 deps 배열 넣기
  • deps 배열 안에 넣은 내용이 바뀌면 함수 호출, 내용이 바뀌지 않으면 이전 값을 재사용

useMemo 사용 전 : input 값을 바꿀 때에도 countActiveUsers 함수가 호출됨 (불필요할 때에도 호출하여서 자원 낭비)

const countActiveUsers = () => {
    console.log('count active users..');
    return users.filter(user => user.active).length;
}

const count = countActiveUsers(users);
return (
    <>
      <CreateUser
        username={username}
        email={email}
        onChange={onChange}
        onCreate={onCreate}
      />
      <UserList users={users} onRemove={onRemove} onToggle={onToggle} />
      <div>활성사용자 수 : {count}</div>
    </>
  );
  • ex) 이메일을 입력한다고 치면 CreateUser 컴포넌트의 email이라는 속성(props)가 바뀌게 되는데 이 값은 원래 App 컴포넌트의 state임
  • 결국 input에 입력하는 행위는 App Component의 state를 바꾸게 되는 행위
  • App의 state가 변경됐으니 App Component가 다시 렌더링되면서 countActiveUsers가 실행되면서 호출됨

useMemo 사용 : users에 변화가 있을 때만 countActiveUsers 함수 호출해서 성능 최적화

const countActiveUsers = () => {
    console.log('count active users..');
    return users.filter(user => user.active).length;
}

const count = useMemo(() => countActiveUsers(users), [users]);
  return (
    <>
      <CreateUser
        username={username}
        email={email}
        onChange={onChange}
        onCreate={onCreate}
      />
      <UserList users={users} onRemove={onRemove} onToggle={onToggle} />
      <div>활성사용자 수 : {count}</div>
    </>
  );

useCallback : useMemo를 기반으로 만들어져 성능 최적화할 때 사용

const onToggle = useMemo(
  () => () => {
    /* ... */
  },
  [users]
);
  • 위랑 같은 건데, 함수를 위해서 사용할 때 더욱 편하게 사용 가능

userCallback 사용 전 : 함수들은 컴포넌트가 리렌더링될 때마다 새로 만들어짐

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

  setInputs({
    username: '',
    email: ''
  });
  nextId.current += 1;
};

const onRemove = id => {
  // user.id 가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듬
  // = user.id 가 id 인 것을 제거함
  setUsers(users.filter(user => user.id !== id));
};
const onToggle = id => {
  setUsers(
    users.map(user =>
      user.id === id ? { ...user, active: !user.active } : user
    )
  );
};

userCallback 사용 : 함수는 필요할 때만 한 번 새로 만들고 재사용 (최적화 위함)

const onCreate = useCallback(() => {
    const user = {
      id: nextId.current,
      username,
      email
    };
    setUsers(users.concat(user));

    setInputs({
      username: '',
      email: ''
    });
    nextId.current += 1;
  }, [users, username, email]);

const onRemove = useCallback(
    id => {
      // user.id 가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듬
      // = user.id 가 id 인 것을 제거함
      setUsers(users.filter(user => user.id !== id));
    },
    [users]
);
const onToggle = useCallback(
    id => {
      setUsers(
        users.map(user =>
          user.id === id ? { ...user, active: !user.active } : user
        )
      );
    },
    [users]
);

React.memo : 컴포넌트의 리렌더링 성능 최적화할 때 사용

  • 컴포넌트에서 리렌더링이 필요한 상황에서만 리렌더링 하도록 설정
  • 컴포넌트의 props가 바뀌지 않았다면 리렌더링을 방지 (props 비교해서)
export default React.memo(CreateUser);
export default React.memo(UserList);
  • input을 수정할 때 하단의 UserList가 리렌더링되지 않게 됨
  • 그런데, User 중 하나라도 수정하면 모든 User들이 리렌더링되고, CreateUser도 리렌더링됨
  • why? deps 에 users 가 들어있어서 users 배열이 바뀔 때마다 onCreate, onToggle, onRemove 함수가 새로 만들어지기 때문
    -> deps 에서 users 를 지우고, 함수들에서 현재 useState 로 관리하는 users 를 참조하지 않게 하기 (함수형 업데이트)
const onCreate = useCallback(() => {
    const user = {
      id: nextId.current,
      username,
      email
    };
    setUsers(users => users.concat(user));

    setInputs({
      username: '',
      email: ''
    });
    nextId.current += 1;
  }, [username, email]);

  const onRemove = useCallback(id => {
    // user.id 가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듬
    // = user.id 가 id 인 것을 제거함
    setUsers(users => users.filter(user => user.id !== id));
  }, []);
  const onToggle = useCallback(id => {
    setUsers(users =>
      users.map(user =>
        user.id === id ? { ...user, active: !user.active } : user
      )
    );
  }, []);
  • setUsers 에 등록하는 콜백함수의 파라미터에서 최신 users 를 참조할 수 있기 때문에 deps 에 users 를 넣지 않아도 됨
  • 실제로 렌더링을 방지할 수있는 상황에만 React.memo 사용할 것 (불필요한 props 비교 방지)
  • React.memo 에서 두 번째 파라미터에 propsAreEqual 함수를 사용하여 특정 값들만 비교하는 것도 가능
export default React.memo(
  UserList,
  (prevProps, nextProps) => prevProps.users === nextProps.users
);

학습 사이트: https://react.vlpt.us/basic/19-React.memo.html

  1. useEffect를 사용하여 마운트/언마운트/업데이트시 할 작업 설정하기
  2. useMemo 를 사용하여 연산한 값 재사용하기
  3. useCallback 을 사용하여 함수 재사용하기
  4. React.memo 를 사용한 컴포넌트 리렌더링 방지