앞부분 내용은 https://memang828.tistory.com/55
TIL 34일차 항해 심화주차 React강의 흐름 정리 (툴킷 ,Json server, axios, Thunk, react hook
강의 학습 위주로 공부했기 때문에 에러 슈팅 느낌으로 쓰기보다는, 강의의 흐름을 요약하려고 한다. 심화주차는 결국 리액트를 배운 팀원끼리 힘을 합쳐서 우리팀만의 react app을 만드는 것이다
memang828.tistory.com
서버와 연결하는 법을 Axios 그리고 redux-toolkit 에 있는 thunk를 통해 연결 확인 및 미들웨어를 활용하는 것을 배웠다.
06. React Hooks : optimizing
만들었던 리엑트 파일을 최적화해서 성능을 올릴 필요가 있음
이때 사용하는게 memo() useCallback, , useMemo 이다.
위 3가지는 컴포넌트의 불필요한 리렌더링을 하지 않도록 해주는 함수입니다. 불필요한 리렌더링이란, 화면에서 변경되는 부분이 없음에도 불구하고 아래의 이유로 화면이 다시 렌더링 되는 것을 말합니다. 불필요한 렌더링을 줄이는 것으로 리액트 프로젝트의 부하를 줄이고, 퍼포먼스 능력을 향상시킬 수 있습니다
리랜더링의 조건
- 부모 컴포넌트가 렌더링 된 경우
- 컴포넌트의 state가 변경된 경우 --> memo()
- 부모로부터 전달받은 props 값이 변경된 경우 --> useCallback()
1. memo()
우선 App.js와 Button 컴퍼넌트를 만듭니다.
//App.jsx
import React, { useState } from "react";
import Button from "./components/Button";
const App = () => {
const [value, setValue] = useState("");
const onChangeHandler = (e) => {
setValue(e.target.value);
};
return (
<div>
<input type="text" value={value} onChange={onChangeHandler} />
<Button />
</div>
);
};
export default App;
// components/Button.js
import React from "react";
const Button = () => {
console.log("리렌더링되고 있어요.");
return <button>버튼</button>;
};
export default Button;
input 에 값을 넣으면 Button.js 에 있는 리렌더링되고 있어요. 라는 텍스트가 콘솔에 찍히는 것을 볼 수 있습니다.
App.js가 랜더링 되는 것은 input에서 onChange 로 setValue를 계속 실행중이라서 불필요한 랜더링이 아닙니다.
불필요한 리렌더링은 Button.js 에서 발생하고 있습니다. input value의 변화와는 아무 관련이 없는 Button이 컴포넌트의 state가 변경된 이유로 계속 렌더링되고 있음
이때 메모를 사용합니다
(memo를 import한 후에, 버튼을 export할 때 memo(Button)
import React, { memo } from "react";
const Button = () => {
console.log("리렌더링되고 있어요.");
return <button>버튼</button>;
};
export default memo(Button);
2. useCallback()
만약 우리가 Button.js를 부모에서 동작하게 하고자 어떤 함수를 props로 넘겨주면 다시 Button.js는 리렌더링을 하기 시작합니다 memo()를 사용해도 Button.js가 다시 리렌더링을 하게된 원인은 함수를 props로 전달 받았기 때문입니다. 전달받은 props가 “3부모로부터 전달받은 props 값이 변경된 경우” 에 해당되도록 원인을 제공합니다
// src/components/Button.js
import React, { memo } from "react";
const Button = ({ onClick }) => {
console.log("리렌더링되고 있어요.");
return <button onClick={onClick}>버튼</button>;
};
export default memo(Button);
// src/App.jsx
import React, { useState } from "react";
import Button from "./components/Button";
const App = () => {
// App.js가 리렌더링 될때마다 재생성됨
const [value, setValue] = useState("");
// App.js가 리렌더링 될때마다 재생성됨
const onChangeHandler = (e) => {
setValue(e.target.value);
};
// App.js가 리렌더링 될때마다 재생성됨
const onClickHandler = () => {
console.log("hello button!");
};
return (
<div>
<input type="text" value={value} onChange={onChangeHandler} />
{/* 매번 재생성되는 함수를 props로 넘겨줌 -> Button.js 리렌더링 유발 */}
<Button onClick={onClickHandler} />
</div>
);
};
export default App;
App.js에서부터 Button.js 까지 input에 값을 넣을 때 마다 어떤식으로 컴포넌트들이 동작하고 있는지 아래에 순서대로 나열해보겠습니다.
- input에 값을 입력
- onChangeHandler가 실행되고, setValue가 실행 → value라는 state가 변경됨
- state 가 변경됨에 따라 App.js가 리렌더링 됨
- App.js 가 리렌더링되면, App.js 안에 있는 useState 및 함수들이 다시 생성됨
- 함수란? → onChangeHandler, onClickHandler
- onClickHandler 이 재선언되었기 때문에 Button.js 입장에서는 새로운 값으로 판단함
- Button.js은 onClickHandler 함수를 새로운 props 로 인식하고 리렌더링 함
- Button.js이 리렌더링되면서 console.log("리렌더링되고 있어요."); 이 실행됨
따라서 함수를 매번 재생성하지 않게 하면, 리랜더링을 막을 수 있습니다.
useCallback 이란?
컴포넌트가 리렌더링되더라도 생성된 함수를 새로 만들지 않고, 재사용하고자 할때 사용하는 훅
useCallback을 사용한 코드
import React, { useCallback, useState } from "react";
import Button from "./components/Button";
const App = () => {
const [value, setValue] = useState("");
const onChangeHandler = (e) => {
setValue(e.target.value);
};
const onClickHandler = useCallback(() => {
console.log("hello button!");
}, []);
return (
<div>
<input type="text" value={value} onChange={onChangeHandler} />
<Button onClick={onClickHandler} />
</div>
);
};
export default App;
함수 선언 부분에 useCallback( , []) 를 넣어주면 된다.
3. useMemo
useMemo는 useCallback과 똑같은 기능을 하는 훅입니다. 다만, 대상이 함수가 아니라 배열이나 객체와 같은 값일 때 사용합니다
예시) data를 props로 내리는 경우
// src/App.jsx
import React, { useState } from "react";
import List from "./components/List";
const App = () => {
const [value, setValue] = useState("");
const onChangeHandler = (e) => {
setValue(e.target.value);
};
const data = [
{
id: 1,
title: "react",
},
];
return (
<div>
<input type="text" value={value} onChange={onChangeHandler} />
<List data={data} />
</div>
);
};
export default App;
// src/components/List.jsx
import React, { memo } from "react";
const List = ({ data }) => {
console.log("리렌더링되고 있어요.");
return (
<div>
{data.map((todo) => (
<div key={todo.id}>{todo.title}</div>
))}
</div>
);
};
export default memo(List);
App.js 에서 data라는 배열을 List라는 자식 컴포넌트에 넘겨주고 있습니다. List가 memo를 사용하고 있음에도 계속 리렌더링이 될텐데요. 그 원인은 props로 받은 배열이 App.js가 리렌더링될때마다 재생성되고 있기 때문입니다.
useCallback에서 했던 것과 마찬가지로 data 라는 배열을 useMemo를 통해서 재성생되지 않도록 해줍니다.
const data = useMemo(() => {
return [
{
id: 1,
title: "react",
},
];
}, []);
data를 useMemo( ,[]) 로 넘기면 된다.
4. 주의사항
최적화를 위해 함수를 사용하지만, 너무 무분별하게 사용하면 오히려 함수때문에 코드가 무거워진다.
07. Custom Hook
리액트에서는 반복되는 로직이나 중복되는 코드를 우리만의 훅, 즉 커스텀 훅을 통해서 관리할 수 있습니다. 리액트에서 제공하는 useState, useEffect와 같은 내장 훅을 사용해서 우리만의 훅을 만드는 것이죠.
#인풋을 관리하는 훅인 useInput을 만들어보자
input을 관리하는 훅이니까 useInput이라고 이름을 지은 것뿐입니다. 커스텀 훅을 만들때 이름은 내 마음대로 해도 상관이 없으나, 단! 파일의 이름 앞에 use 라는 키워드를 붙여줘야 합니다. 보통 hooks라는 폴더에 훅을 따로 모아놓음.
// src/hooks/useInput.js
import React, { useState } from "react";
const useInput = () => {
// 2. value는 useState로 관리하고,
const [value, setValue] = useState("");
// 3. 핸들러 로직도 구현합니다.
const handler = (e) => {
setValue(e.target.value);
};
// 1. 이 훅은 [ ] 을 반환하는데, 첫번째는 value, 두번째는 핸들러를 반환합니다.
return [value, handler];
};
export default useInput;
value에 값을 저장하고, handler를 통해 value값을 수정함 (handler안에 setValue 로)
.
App.js에 아래와 같이 만든다.
// src/App.jsx
import React from "react";
import useInput from "./hooks/useInput";
const App = () => {
// 우리가 만든 훅을 마치 원래 있던 훅인것마냥 사용해봅니다.
const [title, onChangeTitleHandler] = useInput();
const [body, onChangeBodyHandler] = useInput();
return (
<div>
<input
type="text"
name="title"
value={title}
onChange={onChangeTitleHandler}
/>
<input
type="text"
name="title"
value={body}
onChange={onChangeBodyHandler}
/>
</div>
);
};
export default App;
value 의 이름은 각각 title, body 핸들러의 이름은 각각 onChangeTitleHandler, onChangerBodyHandler로 지음
커스텀 훅을 사용하지 않았따면 아래와 같이 된다.
// src/App.jsx
import React from "react";
import { useState } from "react";
const App = () => {
// input의 갯수가 늘어날때마다 state와 handler가 같이 증가한다.
const [title, setTitle] = useState("");
const onChangeTitleHandler = (e) => {
setTitle(e.target.value);
};
// input의 갯수가 늘어날때마다 state와 handler가 같이 증가한다.
const [body, setBody] = useState("");
const onChangeBodyHandler = (e) => {
setBody(e.target.value);
};
return (
<div>
<input
type="text"
name="title"
value={title}
onChange={onChangeTitleHandler}
/>
<input
type="text"
name="body"
value={body}
onChange={onChangeBodyHandler}
/>
</div>
);
};
export default App;
08. 배포
이 부분은 당장 프로젝트 진행에는 지장이 없으므로 추후 과제완료 후 배포시에 따로 정리하겠습니다.
'TIL > 항해산 TIL' 카테고리의 다른 글
| 37일차 react로 페이지 만들기 2일차 -옵셔널 체이팅 (0) | 2023.01.10 |
|---|---|
| 36일 TIL React로 만든 나의 ~~ 사이트 1일차 (0) | 2023.01.09 |
| TIL 34일차 -2 json서버 아이디 생성 규칙 (1) | 2023.01.07 |
| TIL 34일차 항해 심화주차 React강의 흐름 정리 (툴킷 ,Json server, axios, Thunk, react hook (0) | 2023.01.07 |
| TIL 33일차 axios 로 (post, delete ,patch)이후 랜더링하기 (0) | 2023.01.06 |