useCallback
useCallback을 사용해야하는 이유는 다음과 같다.
다음과 같은 코드에서 input 으로 value 라는 state 를 변경하게 되면 리렌더가 많이 발생하게 된다.
이때 h1 태그 onClick 에 있는 () => console.log('aa') 함수는 리렌더링 될때마다 다시 생성되게 된다.
이는 간단한 함수일때는 괜찮지만 함수의 덩치가 커지면 문제를 야기할 수 있게 된다.
import "./styles.css";
import React, { useState, useCallback } from "react";
export default function App() {
const [value, setValue] = useState("");
const onChangeValue = (e) => {
setValue(e.target.value);
};
return (
<>
<div className="App">
<h1 onClick={() => console.log("aa")}>dd</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
<input value={value} onChange={onChangeValue} />
</>
);
}
이러한 문제를 해결하기위해 사용하는 것이 useCallback 이다.
...
export default function App() {
...
const consoleConsole = useCallback(() => {
console.log("aa");
}, []);
return (
<>
<div className="App">
<h1 onClick={consoleConsole}>dd</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
<input value={value} onChange={onChangeValue} />
</>
);
}
또한 이러한 useCallback 을 사용하지 않고 props 로 함수를 물려줄 경우에 , 부모가 리렌더 되면 함수도 다시 생성되기 때문에 props 가 업데이트 되어 자식 컴포넌트도 의도치 않게 리렌더링 되는 현상이 발생할 수 있다.
useMemo
다음과 같은 코드를 볼때 마찬가지로 input 에 문자를 입력하게 되면 다음 코드가 계속 재실행되게 된다.
prices.reduce((a, c) => a + c, 0)}
import "./styles.css";
import React, { useState, useCallback } from "react";
export default function App() {
const [value, setValue] = useState("");
const [prices, setPrices] = useState(
Array(100)
.fill()
.map((v, i) => (i + 1) * 100)
);
const onChangeValue = (e) => {
setValue(e.target.value);
};
return (
<>
<div className="App">
<h1>dd</h1>
<h2>{prices.reduce((a, c) => a + c, 0)}</h2>
</div>
<input value={value} onChange={onChangeValue} />
</>
);
}
useMemo 를 사용해서 해당 값을 메모이제이션 해줘야 리렌더시 최적화가 된다.
import "./styles.css";
import React, { useState, useCallback, useMemo } from "react";
export default function App() {
const [value, setValue] = useState("");
const [prices, setPrices] = useState(
Array(100)
.fill()
.map((v, i) => (i + 1) * 100)
);
const onChangeValue = (e) => {
setValue(e.target.value);
};
const totalPrice = useMemo(() => {
return prices.reduce((a, c) => a + c, 0);
}, [prices]);
return (
<>
<div className="App">
<h1>dd</h1>
<h2>{totalPrice}</h2>
</div>
<input value={value} onChange={onChangeValue} />
</>
);
}
하지만, Trade Off 는 존재한다.
useMemo , useCallback 모두 리렌더시에 불필요한 연산을 하지 않아도 된다는 이점이 있지만, 리렌더가 발생할때마다 deps 내부의 값이 바뀌었는지 바뀌지 않았는지 검증을 해야하는 과정이 추가된다.
createSelector
리덕스에서는 이러한 Trade Off 에서 벗어나기 위해 reselector 기능을 제공한다.
다음과 같은 initialState가 있다고 하자.
const initialState = {
isLoggingIn: false,
data: null,
prices: Array(100)
.fill()
.map((v, i) => (i + 1) * 100),
};
해당값에 다음처럼 접근할 수 있게 되는데,
const prices = useSelector((state) => state.user.prices);
useSelector 내부의 순수함수를 따로 빼서 컴포넌트 밖으로 빼보자.
다음처럼 말이다.
이렇게 빼서 사용해도 이전의 결과와 다르지 않다.
const priceSelector = (state) => state.user.prices;
const App = () => {
...
const prices = useSelector(priceSelector);
}
createSelector 사용
다음처럼 아까 빼둔 priceSelector 를 사용해서 sumPriceSelector를 만들자.
이렇게 되면 이전과 결과 값이 같지만 createSelector 를 사용하게 되므로 메모이제이션을 사용하게 된다.
const priceSelector = (state) => state.user.prices;
const sumPriceSelector = createSelector(priceSelector, (prices) =>
prices.reduce((a, c) => a + c, 0)
);
const App = () => {
...
const prices = useSelector(sumPriceSelector);
순수함수가 아닌 함수 컴포넌트 밖으로 빼는 방법
순수 함수란 동일한 매개변수가 주어질때 항상 같은 결과를 리턴하는 함수이다.
다음과 같은 함수를 뺴는 방법은 어떤 방법이 있을까?
const onClick = useCallback(() => {
dispatch(
logIn({
id: "zerocho",
password: "비밀번호",
})
);
}, []);
다음과 같이 고차함수를 사용해서 뺄 수 있다.
const outside = (dispatch) => () => {
dispatch(
logIn({
id: "zerocho",
password: "비밀번호",
})
);
};
const onClick = useCallback(outside(dispatch), []);
고차함수란?
함수를 매개변수로 사용하거나 함수를 반환하는 함수.
'공부기록 > 웹 개발' 카테고리의 다른 글
HTML input에 focus주고, 마지막 글자에 커서 위치 (0) | 2022.09.05 |
---|---|
[eslint] Failed to load config "react-app" to extend from. (0) | 2022.09.05 |
Styled-components로 CSS 덮어쓰기 (feat.createGlobalStyle) (0) | 2022.09.02 |
png 파일 색 변경하는 방법 (0) | 2022.08.31 |
조금의 sass 그리고 css module (0) | 2022.08.29 |