리렌더링이 자주 발생하는 것은 비용이 많이 소모되는데, 이를 최소화하기 위해 최적화가 필요함
최적화 방법:
1) memo(React.memo): 컴포넌트를 캐싱하여 불필요한 렌더링을 방지한다.
2) useCallback: 함수를 캐싱하여 렌더링 시에 새로운 함수를 생성하는 비용을 줄인다.
3) useMemo: 값을 캐싱하여 렌더링 시에 새로운 값이 계산되는 비용을 최소화한다.
리렌더링의 발생 조건 중 부모 컴포넌트가 리렌더링될 때 자식 컴포넌트도 모두 리렌더링되는 상황이 생김
React.memo는 자식 컴포넌트의 불필요한 리렌더링을 방지하기 위해 React에서 제공하는 메소드로, 컴포넌트의 리렌더링을 최적화하는데 사용됨
예시로 부모 컴포넌트가 리렌더링될 때 자식 컴포넌트가 변경되지 않았다면, 해당 자식 컴포넌트는 다시 렌더링되지 않도록 도와준다.
export default React.memo(Box1);
//App.jsx
import { useState } from "react";
import "./App.css";
import Box1 from "./components/Box1";
import Box2 from "./components/Box2";
import Box3 from "./components/Box3";
function App() {
const [count, setCount] = useState(0);
console.log("App 컴포넌트가 렌더링 되었어요!");
//1증가
const onPlusButtonClickHandler = () => {
setCount(count + 1);
};
//1감소
const onMinusButtonClickHandler = () => {
setCount(count - 1);
};
return (
<>
<h3>카운터</h3>
<p>현재 카운트 : {count}</p>
<button onClick={onPlusButtonClickHandler}>+</button>
<button onClick={onMinusButtonClickHandler}>-</button>
<div style={{ display: "flex", marginTop: "10px" }}>
<Box1 />
<Box2 />
<Box3 />
</div>
</>
);
}
export default App;
//Box1.jsx
import React from "react";
function Box1() {
const style = {
width: "100px",
height: "100px",
backgroundColor: "#01c49f",
color: "white",
};
console.log("Box1 컴포넌트가 렌더링 되었어요!");
return <div style={style}>Box1</div>;
}
export default React.memo(Box1);
//Box2.jsx
import React from "react";
function Box2() {
const style = {
width: "100px",
height: "100px",
backgroundColor: "#4e93ed",
color: "white",
};
console.log("Box2 컴포넌트가 렌더링 되었어요!");
return <div style={style}>Box2</div>;
}
export default React.memo(Box2);
//Box3.jsx
import React from "react";
function Box3() {
const style = {
width: "100px",
height: "100px",
backgroundColor: "#c491be",
color: "white",
};
console.log("Box3 컴포넌트가 렌더링 되었어요!");
return <div style={style}>Box3</div>;
}
export default React.memo(Box3);
const initCount = useCallback(() => {
console.log(`${count}에서 0으로 변경되었습니다.`);
setCount(0);
}, [count]);
☞ 의존성 배열[]이 필요한 이유 : 함수가 참조하는 외부 변수가 있다면, 해당 변수를 의존성 배열에 추가하여 그 값이 변경될 때만 함수가 다시 만들어지도록 함
결론: useCallback을 사용하면 함수가 메모이제이션되어, 같은 함수가 반복해서 생성되지 않고 필요한 경우에만 새로 생성되어 성능을 최적화할 수 있음
###UseMemo
###UseMemo 사용법
const value = useMemo(()=> {
return 반환할_함수()
}, [dependencyArray]);
사용 예시: const value = useMemo(() => calculateValue(), [dependencyArray]);
useMemo를 적용하면 좋은 경우
1. 무거운 계산이 필요한 컴포넌트에서 useMemo를 사용하여 계산 결과를 캐싱해서 불필요한 렌더링을 피하고 성능 향상.
! 주의사항
###. 생명주기(LifeCycle)
1) 각 메서드 소개
constructor:
컴포넌트가 처음 만들어질 때 호출되는 생성자 메서드.
getDerivedStateFromProps:
부모 컴포넌트로부터 전달받은 props를 사용하여 state를 설정하는 역할을 하는 메서드.
render:
컴포넌트를 렌더링하는 메서드.
componentDidMount:
컴포넌트가 브라우저에 표시된 후 호출되는 메서드.
shouldComponentUpdate:
리렌더링 여부를 판단하는 메서드. true인 경우 리렌더링 진행, false인 경우 리렌더링 하지 않음.
render:
변경사항 반영이 준비된 후 호출되는 렌더링 메서드.
getSnapshotBeforeUpdate:
컴포넌트에 변화가 일어나기 직전 DOM의 상태를 저장하는 메서드.
componentDidUpdate:
컴포넌트 업데이트 작업이 완료된 후 호출되는 메서드.
[부연설명] 함수형 컴포넌트에서는 클래스형 컴포넌트의 생명주기 메서드를 대체하기 위해 useEffect를 사용함.
특징:
2) Global State (전역 상태)
정의: 중앙 state 관리소(예: 리덕스)에서 생성되고 관리되는 전역적인 상태.
범위: 모든 컴포넌트에서 접근 가능하며, 중앙 state 관리소를 통해 상태를 공유함.
특징:
3) Global state와 Local state의 간단한 비교
Local State: 한 컴포넌트 내에서만 사용되는 상태로, 해당 컴포넌트의 내부에서만 유효하다.
Global State: 어플리케이션 전체에서 사용되는 상태로, 모든 컴포넌트에서 접근 가능하며 중앙 state 관리소를 통해 관리된다.
yarn add redux react-redux
//아래와 같은 의미
yarn add redux
yarn add react-redux
redux : 리덕스 관련 모든 코드가 담긴 폴더
config : 리덕스 설정 관련 모든 파일
configStore : 중앙 state 관리소 => 설정 코드(js)
modules : 중앙 state에서 관리하는 state의 그룹
1) 설정 코드 작성 시, 주의사항
2) src/configStore.js
-스토어를 생성하는 핵심 메소드인 createStore를 이용하여 스토어를 만든다.
//src/configStore.js
//중앙 데이터 관리소를 설정하는 방법
import { createStore } from "redux";
import { combineReducers } from "redux";
const rootReducer = combineReducers({
});
const store = createStore(rootReducer);
export default store;
3) index.js
//index.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { Provider } from "react-redux";
import store from "./redux/config/configStore";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<Provider store={store}>
<App />
</Provider>
);
4) 모듈 counter
(1) initialState === 초기 상태값
모듈에서 사용할 초기 상태값을 정의한다.
(2) Reducer === 변화를 일으키는 함수
리듀서는 변화를 일으키는 함수로, 모듈의 상태를 어떻게 변경할지 정의한다.
(3) 카운터 모듈을 스토어에 연결하기
생성한 모듈을 스토어와 연결하여 사용할 수 있도록 설정한다.
//중앙 데이터 관리소를 설정하는 방법
import { createStore } from "redux";
import { combineReducers } from "redux";
import counter from "../modules/counter"; // <<<여기
const rootReducer = combineReducers({
counter, // <<<여기
});
const store = createStore(rootReducer);
export default store;
5) 스토어와 모듈 연결하기
(1) useSelector = 스토어 조회
useSelector 훅을 사용하여 컴포넌트에서 필요한 상태값을 컴포넌트에서 조회할 수 있다.
import logo from "./logo.svg";
import "./App.css";
import { useDispatch, useSelector } from "react-redux"; //
function App() {
const counter = useSelector((state) => {
return state.counter;
});
const dispatch = useDispatch();
return (
<div className="App">
<div>현재 카운트 : {counter.number}</div>
</div>
);
}
export default App;
1) State 변경: 리덕스에서 state를 변경하려면 dispatch를 통해 액션 객체를 리듀서로 보내야 함.
2) 액션 객체 생성: 액션 객체는 type을 가진 객체로, 리듀서에 명령을 전달하는 역할.
3) Dispatch: useDispatch 훅을 이용하여 액션 객체를 리듀서로 보내는 함수 생성.
4) 액션 객체 수신(받기): 리듀서에서 console.log(action) 등으로 액션 객체를 확인 가능.
5) 리듀서 로직 구현: 액션 객체의 type에 따라 state 변경 로직을 리듀서에 구현
액션객체
반드시 type이라는 key를 가져야 하는 객체로, 리듀서에게 전달되어 상태를 변경하는 명령을 포함한다.
디스패치
액션 객체를 리듀서로 보내는 함수로, useDispatch 훅을 사용하여 컴포넌트 내에서 생성한다.
리듀서
디스패치를 통해 전달받은 액션 객체를 검사하고, 조건에 따라 새로운 상태값을 생성하는 함수이다.
대문자
액션 객체의 type 값은 대문자로 작성하는 것이 관례이다.
오늘의 한줄평 : 역시 리덕스가 어렵다고 하던데 역시, 조금 어려웠다.