[React] useEffect활용, 렌더링 최적화

cool_kim·2021년 7월 18일
0

React

목록 보기
7/8
post-thumbnail

📌 useEffect 활용하기

function Profile({userId}) {
	const [user, setUser] = useState();
	useEffect(() => {
		fetchUser(userId).then(data => setUser(data));
	}, [userId]);
}

⇒ 마운트 된 후 한번만 호출할 수 있도록 useEffect 의존성 배열에 빈배열 입력할 수 있지만 좋은 방법 ❌
⇒ userId가 바뀔때 렌더링 할 수 있도록 의존성 배열에 userId를 입력해 줘야함

💡 마운트 되었을 때만 실행되게 하려면 별도의 훅을 사용하는 것이 더 좋음

function Profile({ userId }) {
  const [user, setUser] = useState();
  useOnMounted(() => fetchUser(userId).then(data => setUser(data)))
}

💡 useEffect에서 async await함수를 사용하고 싶을 때
(async await함수는 Promise객체를 반환하기 때문에 useEffect에서 사용할 수 없음. useEffect는 함수만 반환 가능)

function Profile({ userId }) {
	const [user, setUser] = useState();
	useEffect(() => {
		async function fetchAndSetUser(needDetail) {
			const data = await fetchUser(userId, needDetail);
			setUser(data);
	}
	fetchAndSetUser(false);
}, [userId]);

⇒ 위 코드에서 userId가 변경될 때만 useEffect함수가 렌더 되게 하려면 useCallback훅 사용

✔️ 의존성 배열은 되도록 사용하지 않는것이 좋음(∴ 관리하는데에 많은 시간과 노력 필요)



💡 여러 상태값을 참조하면서 값 변경할 때 → useReducer훅 사용

function Timer({ initialTotalSeconds }) {
  const [state, dispatch] = useReducer(reducer, {
    hour: initialTotalSeconds / 3600
    minute: (initialTotalSeconds % 3600) / 60
    second: initialTotalSeconds % 60
  });
  const { hour, minute, second } = state;
  useEffect(() => {
    const id = setInterval(dispatch, 1000);
    return () => clearInterval(id);
  });
  //...
}

function reducer(state) {
  const { hour, minute, second } = state;
  if(second) {
    return { ..state, second: second - 1 };
  } else if(second) {
    return { ..state, minute: minute - 1, second: 59 };
  } else if(second) {
    return { ..state, hour: hour - 1, minute: 59, second: 59 };
  } else {
    return state;
  }
}

→ dispatch함수는 변경되지 않는 값이므로 의존성 배열에 입력하지 않아도 됨

💡 의존성 배열을 관리하는데에 마땅한 해결책이 생각나지 않는 경우 → useRef 훅 사용

function MyComponent({ onClick }) {
  const onClickRef = useRef();
  
  // concurrent mode를 위한 작성법
  useEffect(() => {
    onClickRef.current = onClick;
  });
  
  useEffect(() => {
    window.addEventListener('click', () => {
      onClickRef.current();
      // ...
    })
    // ...
  });
  // ...
}

→ ref객체는 컴포넌트 안에서 직접 수정하면 안됨! (concurrent mode 때문)




📌 렌더링 속도 올리는 최적화 방법

리액트의 대부분의 연산 : 컴포넌트 함수의 실행 & 가상돔에서 발생

📍 React.memo사용시
memo로 감싼 컴포넌트에 두번째 인자로 비교함수를 넣어주면 그 값을 토대로 돔에 반영함

function MyComponent(props) {
  // ...
}
function isEqual(prefProps, nextProps) {
  // true 또는 false를 반환
}
React.memo(MyComponent, isEqual)

(두번째 인자로 비교 연산을 하지 않아도 되지만 넣어주면 렌더링 속도가 빨라짐)



❓ 이전 값과 현재값이 다르다는 것을 어떻게 비교
➡️ === 연산자로 비교! (객체를 불변 객체로 관리)
prevProps.todos === nextProps.todos



속성값 관리

💡 useCallback훅 사용

//...
const onChangeFruit = useCallback(fruit => {
	setSelectedFruit(fruit);
	sendLog({ type: 'fruit change', value: fruit });
}, []);
//...
	<SelectFruit selected={selectedFruit} onchange={onchangeFruit} />


속성값이 변경되어야 하는데 변경하지 않는 경우

💡상태값의 레퍼런스가 변경되지 않기 때문
⇒ 상태값을 불변객체로 관리 해야함

profile
FE developer

0개의 댓글