강의를 통해 배운 점
1달만에 강의 수강을 다시 시작했다. 그러다보니 클래스 컴포넌트 만드는게 살짝 잊혀져서 “어떻게하더라?” 라며 멍때리게 되더라. 노션에 메모 해둔 시리즈를 보면서 다시 기억을 되살릴 수 있었는데, 정말 노션과 블로깅은 개발자에게 필수인것 같다. 한번 습득했던 기술은 언제든 기억에서 사라질 수 있고, 그럴 때 기록했던 내용을 보면서 복기하면 다시 기억을 되살릴 수 있다는걸 깨달았다.
노션 기록과 블로깅이 꽤나 귀찮은 일이지만 최대한 열심히 해야함을 제대로 느꼈다.
클래스 컴포넌트 (강의코드)
import React, { Component } from 'react';
import Ball from './Ball';
import { getWinNumbers } from './getWinNumbers';
class LottoClass extends Component {
state = {
winNumbers: getWinNumbers(), // 당첨 숫자들
winBalls: [],
bonus: null, // 보너스 공
redo: false,
};
timeouts = [];
runTimeouts = () => {
const { winNumbers } = this.state;
for (let i = 0; i < winNumbers.length - 1; i++) {
this.timeouts[i] = setTimeout(() => {
this.setState(prevState => {
return { winBalls: [...prevState.winBalls, winNumbers[i]] };
});
}, (i + 1) * 100);
this.timeouts[6] = setTimeout(() => {
this.setState({
bonus: winNumbers[6],
redo: true,
});
}, 700);
}
};
componentDidMount() {
this.runTimeouts();
}
componentDidUpdate() {
if (this.state.winBalls.length === 0) {
this.runTimeouts();
}
}
componentWillUnmount() {
this.timeouts.forEach(x => clearTimeout(x));
}
onClickRedo = () => {
this.setState({
winNumbers: getWinNumbers(), // 당첨 숫자들
winBalls: [],
bonus: null, // 보너스 공
redo: false,
});
this.timeouts = [];
};
render() {
const { winBalls, bonus, redo } = this.state;
return (
<>
<div>당첨숫자</div>
<div id="결과창">
{winBalls.map(x => (
<Ball key={x} number={x} />
))}
</div>
<div>보너스!</div>
{bonus && <Ball number={bonus} />}
{redo && <button onClick={this.onClickRedo}>한번 더!</button>}
</>
);
}
}
export default LottoClass;
함수 컴포넌트 (내 코드)
import React, { useEffect, useRef, useState } from 'react';
import Ball from './Ball';
import { getWinNumbers } from './getWinNumbers';
function LottoHooks() {
const [winNumbers, setWinNumbers] = useState(getWinNumbers);
const [winBalls, setWinBalls] = useState([]);
const [bonus, setBonus] = useState(null);
const [redo, setRedo] = useState(false);
const timeouts = useRef([]);
const redoFlag = useRef(false);
const runTimeOuts = () => {
for (let i = 0; i < 6; i++) {
timeouts.current[i] = setTimeout(() => {
setWinBalls(prev => [...prev, winNumbers[i]]);
}, (i + 1) * 100);
}
timeouts.current[6] = setTimeout(() => {
setBonus(winNumbers[6]);
setRedo(true);
}, 700);
};
useEffect(() => {
runTimeOuts();
return () => timeouts.current.forEach(x => clearTimeout(x));
}, [redoFlag.current]);
const onClickRedo = () => {
setWinNumbers(getWinNumbers);
setWinBalls([]);
setBonus(null);
setRedo(false);
redoFlag.current = !redoFlag.current;
};
return (
<>
<div>당첨숫자 훅스</div>
<div id="결과창">
{winBalls.map(x => (
<Ball key={x} number={x} />
))}
</div>
<div>보너스!</div>
{bonus && <Ball number={bonus} />}
{redo && <button onClick={onClickRedo}>한번 더!</button>}
</>
);
}
export default LottoHooks;
같은 로직이라도 함수 컴포넌트가 훨씬 깔끔하고 좋다. 아무래도 계속 함수 컴포넌트로 개발을 하다보니 훨씬 편하게 느낄지도 모르겠다. 어쨌든 useEffect로 시작시점과 끝시점, 업데이트 시점을 정해줄수 있다보니 훨씬 보기가 쉬워졌다.
개선 코드 1 : useEffect
useEffect(() => ... , [timeouts.current]
useEffect의 디펜던시로 플래그를 줬었는데, 타임아웃츠 또한 새로 시작할 때마다 새로운 [] 배열이 생성되는 거라 같은 역할을 한다. 이를 업데이트 캐치용 디펜던시로 세팅해주면 정상 동작한다.
개선 코드 2 : useMemo, useCallback
사실 필히 개선할 사항은 없어서 코드는 추가하지 않는다. 대신 기록만 남김
1) useMemo, useCallback 모두 메모이제이션을 활용해 return value, return 함수 가 이전과 같으면 ([]디펜던시 배열안의 값이 안바뀌면) 새로 만들지 않는다.
2) 가장 필요한 경우는 Props를 던질때 쓸데없이 새로 생성된 함수 때문에 하위 컴포넌트가 리렌더링이 되는 경우가 있다. 이를 막기 위해 useCallback의 사용이 꼭 필요해진다.
3) useMemo의 경우는 복잡한 연산을 굳이 리렌더링 될때마다 연산하지 않도록 저장해두면 유용하다.
훅스 자잘한 팁들
1) 훅스는 최상위에 나둬야한다. (if, for, 다른 훅스 안에서 사용하지 않기)
'FrontEnd > React.js' 카테고리의 다른 글
[React.js] useCallback 예시 (0) | 2023.02.04 |
---|---|
[React.js] 제로초 슬랙 클론코딩 - 1. 회원가입/로그인 페이지 (SWR) (0) | 2022.12.12 |
[React.js] 코드 스플리팅 (Loadable) (0) | 2022.12.12 |
[React.js] useContext 찍먹, useFetch 구현, loading, error 상태 관리 (0) | 2022.10.31 |
[React.js] axios instance.interceptor를 이용한 미들처리 (0) | 2022.10.29 |
[React.js] 리팩토링 하며 생긴 질문 - axios 의 catch() 와 try-catch의 catch가 같은건가? (0) | 2022.10.28 |
[React.js] 제로초 웹게임 - 5. 가위바위보 (life Cycle, useEffect) (0) | 2022.10.25 |
[React.js] 제로초 웹게임 - 4. 반응속도체크 (useRef) (0) | 2022.10.24 |
댓글