프리온보딩 개념 정리

hodu·2023년 4월 10일
0

프리온 보딩 개념들을 정리하고자한다.


useReducer

useState와 같이 컴포넌트의 상태를 관리할 수 있지만, usestate로 작성하면 로직이 길어지기떄문에 이를 방지할 수 있다.
조건에 대처가 좋다.


import { useReducer } from 'react';

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

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  function handleIncrement() {
    dispatch({ type: 'INCREMENT' });
  }

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={handleIncrement}>Increment</button>
    </div>
  );
}

여기서 reducer 함수는 현재 상태와 액션을 받아서 새로운 상태를 반환하는 함수입니다. 액션은 상태를 변경하기 위한 정보를 담고 있으며, dispatch 함수를 호출할 때마다 이 액션을 전달합니다. reducer 함수에서는 액션의 type 속성을 검사하여 적절한 처리를 수행합니다.

위의 코드에서는 dispatch({ type: 'INCREMENT' })를 호출하여 상태를 변경합니다. dispatch 함수가 호출될 때마다 reducer 함수가 호출되어 새로운 상태가 반환됩니다. 이러한 방식으로 상태를 변경할 수 있습니다.

이와 같이 useReducer를 사용하면, 복잡한 로직을 포함하는 상태 관리를 보다 쉽게 구현할 수 있습니다.

메모이제이션

console.time('filter array');
const visibleTodos = getFilteredTodos(todos, filter);
console.timeEnd('filter array');

측정 중인 상호 작용을 수행합니다(예: 입력에 입력). filter array: 0.15ms그러면 콘솔과 같은 로그가 표시됩니다 . 전체 기록 시간의 합이 상당한 양(예를 들어, 1ms그 이상)인 경우 해당 계산을 메모이제이션하는 것이 합리적일 수 있습니다. 실험으로 계산을 래핑하여 useMemo해당 상호 작용에 대해 기록된 총 시간이 감소했는지 여부를 확인할 수 있습니다.

console.time('filter array');
const visibleTodos = useMemo(() => {
  return getFilteredTodos(todos, filter); // Skipped if todos and filter haven't changed
}, [todos, filter]);
console.timeEnd('filter array');

시스템이 사용자의 시스템보다 빠를 수 있으므로 인위적인 속도 저하로 성능을 테스트하는 것이 좋습니다. 예를 들어 Chrome은 이를 위해 CPU 스로틀링 옵션을 제공합니다.

또한 개발 단계에서 성능을 측정한다고 해서 가장 정확한 결과가 나오는 것은 아닙니다. (예를 들어 Strict Mode가 켜져 있으면 각 구성 요소가 한 번이 아니라 두 번 렌더링되는 것을 볼 수 있습니다.) 가장 정확한 타이밍을 얻으려면 프로덕션용 앱을 빌드하고 사용자가 가지고 있는 것과 같은 장치에서 테스트하십시오.


실제로 useMemo를 측정했을 때 리렌더링이 적게 일어난다면 효율적이지 못하다 실험이 있었다.

mport React from 'react';
const BenchmarkNormal = ({level}) => {
    const complexObject = {
        values: []
    };
    for (let i = 0; i <= level; i++) {
        complexObject.values.push({ 'mytest' });
    }
    return ( <div>Benchmark level: {level}</div>);
};
export default BenchmarkNormal;

이는 일반적인 벤치마크 컴포넌트이며 useMemo를 사용한 BenchmarkMemo 컴포넌트도 만들 것입니다.

const BenchmarkMemo = ({level}) => {
    const complexObject = useMemo(() => {
        const result = {
            values: []
        };

        for (let i = 0; i <= level; i++) {
            result.values.push({'mytest'});
        };
        return result;
    }, [level]);
    return (<div>Benchmark with memo level: {level}</div>);
};
export default BenchmarkMemo;

위 데이터를 참고하면 n = 100 이상 되었을때 효과적이었다.

React 권장사항은 1초였는데, 위 실험을 통해 복잡도가 n=100이상일때 효과적인 것을 알게 되었다.

https://github.com/yeonjuan/dev-blog/blob/master/JavaScript/should-you-really-use-usememo.md?utm_source=substack&utm_medium=email

useEffect의 효용성

Updating state based on props or state

state와 props 기반으로 업데이트할 때

function Form() {
  const [firstName, setFirstName] = useState('Taylor');
  const [lastName, setLastName] = useState('Swift');

  // 🔴 Avoid: redundant state and unnecessary Effect
  const [fullName, setFullName] = useState('');
  useEffect(() => {
    setFullName(firstName + ' ' + lastName);
  }, [firstName, lastName]);
  // ...
}

function Form() {
  const [firstName, setFirstName] = useState('Taylor');
  const [lastName, setLastName] = useState('Swift');
  // ✅ Good: calculated during rendering
  const fullName = firstName + ' ' + lastName;
  // ...
}

Caching expensive calculations

캐싱으로 업데이트
useEffect를 제거하고 계산이 오래걸리는 비싼 로직이라면 useMemo 활용할 수 있다.


function TodoList({ todos, filter }) {
  const [newTodo, setNewTodo] = useState('');

  // 🔴 Avoid: redundant state and unnecessary Effect
  const [visibleTodos, setVisibleTodos] = useState([]);
  useEffect(() => {
    setVisibleTodos(getFilteredTodos(todos, filter));
  }, [todos, filter]);

  // ...
  
  function TodoList({ todos, filter }) {
  const [newTodo, setNewTodo] = useState('');
  // ✅ This is fine if getFilteredTodos() is not slow.
  const visibleTodos = getFilteredTodos(todos, filter);
  // ...
import { useMemo, useState } from 'react';

---

function TodoList({ todos, filter }) {
  const [newTodo, setNewTodo] = useState('');
  // ✅ Does not re-run getFilteredTodos() unless todos or filter change
  const visibleTodos = useMemo(() => getFilteredTodos(todos, filter), [todos, filter]);
  // ...
}

자식으로 렌더링을 최적화

export default function ProfilePage({ userId }) {
  const [comment, setComment] = useState('');

  // 🔴 Avoid: Resetting state on prop change in an Effect
  useEffect(() => {
    setComment('');
  }, [userId]);
  // ...
}

위와 같은 코드를

export default function ProfilePage({ userId }) {
  return (
    <Profile
      userId={userId}
      key={userId}
    />
  );
}

function Profile({ userId }) {
  // ✅ This and any other state below will reset on key change automatically
  const [comment, setComment] = useState('');
  // ...
}

로 개선할 수 있다.

useState vs useEffect

function List({ items }) {
  const [isReverse, setIsReverse] = useState(false);
  const [selection, setSelection] = useState(null);

  // 🔴 Avoid: Adjusting state on prop change in an Effect
  useEffect(() => {
    setSelection(null);
  }, [items]);
  // ...
}

위와 같이 작성하는 것보다

function List({ items }) {
  const [isReverse, setIsReverse] = useState(false);
  const [selection, setSelection] = useState(null);

  // Better: Adjust the state while rendering
  const [prevItems, setPrevItems] = useState(items);
  if (items !== prevItems) {
    setPrevItems(items);
    setSelection(null);
  }
  // ...
}

스테이 값이 업데이트 될때 자동으로 확인한다.
더 효과적으로 상황에 맞게 대처할 수 있다.


function List({ items }) {
  const [isReverse, setIsReverse] = useState(false);
  const [selectedId, setSelectedId] = useState(null);
  // ✅ Best: Calculate everything during rendering
  const selection = items.find(item => item.id === selectedId) ?? null;
  // ...
}

위와 같은 방식으로 찾아주는 방법으로 해결할 수도 있다.

https://react.dev/learn/you-might-not-need-an-effect#caching-expensive-calculations

profile
잘부탁드립니다.

0개의 댓글