ㅇㅇㅈ Blog

프론트엔드 수행중

0%

react-detail

state

useState , useReducer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export default function State() {
const initialCount = 0;
const [count, setCount] = useState(initialCount);
return (
<div>
<hr />
<h3>useState</h3>
Count : {count}
<button onClick={() => setCount(initialCount)}>Reset</button>
<button onClick={() => setCount((prev) => prev - 1)}>-</button>
<button onClick={() => setCount((prev) => prev + 1)}>+</button>
</div>
);
}

버튼을 클릭하면 count가 +,- 가 된다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
export default function Reducer() {
const initialCount = { count: 0, name: 'jimmy' };

const reducer = (state, action) => {
switch (action.type) {
case 'reset':
return initialCount;
case 'increment':
return { count: state.count + 1 , name:'Tom'};
case 'decrement':
return { count: state.count - 1 , name:'Amy'};
}
};
const [state, dispatch] = useReducer(reducer, initialCount);
return (
<div>
<h3>Reducer</h3>
Count : {state.count}
<h4>Name : {state.name}</h4>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
</div>
);
}
  • 컴포넌트에서 관리하는 값이 딱 하나고, 그 값이 단순한 숫자, 문자열 또는 boolean값이라면 useState로 관리하는게 편하다.
  • 컴포넌트에서 관리하는 값이 여러개가 되어서 상태의 구조가 복잡해지면 useReducer로 관리하는게 편하다

Memoization

useMemo 는 특정 결과값을 재사용 할 때 사용하는 반면, useCallback 은 특정 함수를 새로 만들지 않고 재사용하고 싶을때 사용합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//Memo.jsx
const commentList = [
{ title: 'comment1', content: 'message1', likes: 1 },
{ title: 'comment2', content: 'message2', likes: 2 },
{ title: 'comment3', content: 'message3', likes: 3 },
];
export default function Memo() {
const [comments, setComments] = useState(commentList);
useEffect(() => {
const interval = setInterval(() => {
setComments((prev) => [
...prev,
{
title: `comment${prev.length + 1}`,
content: `message${prev.length + 1}`,
likes: 1,
},
]);
}, 5000);
return () => {
clearInterval(interval);
};
}, []);
return <Comments commentList={comments} />;
}

Memo 컴포넌트 5초에 한개씩 리스트가 새로 생성된다
Comments 컴포넌트로 리스트를 props로 넘겨주고있다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Comments.jsx
export default function Comments({ commentList }) {
const handleChange = useCallback(() => {
console.log('눌림');
},[]);
return (
<div>
{commentList.map((comment) => (
<CommentItem
key={comment.title}
title={comment.title}
content={comment.content}
likes={comment.likes}
/>
))}
</div>
);
}

Comments 컴포넌트
Memo로 부터 받은 Props(commentList)로 Map을 이용해
CommentItem 컴포넌트를 반복 생성 하고 있다 이때 props로 key,title,content,likes,onClick을 넘겨 주고 있다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// CommentItem.jsx
import React, { memo, Profiler, useState, useMemo } from 'react';

function CommentItem({ title, content, likes, onClick }) {

function onRenderCallback(
id, // 방금 커밋된 Profiler 트리의 "id"
phase, // "mount" (트리가 방금 마운트가 된 경우) 혹은 "update"(트리가 리렌더링된 경우)
actualDuration, // 커밋된 업데이트를 렌더링하는데 걸린 시간
baseDuration, // 메모이제이션 없이 하위 트리 전체를 렌더링하는데 걸리는 예상시간
startTime, // React가 언제 해당 업데이트를 렌더링하기 시작했는지
commitTime, // React가 해당 업데이트를 언제 커밋했는지
interactions // 이 업데이트에 해당하는 상호작용들의 집합
) {
console.log(`actualDuration`, title, actualDuration);
}

return (
<Profiler id='CommentItem' onRender={onRenderCallback}>
<div className='CommentItem' >
<span>{title}</span> <br />
<span>{content}</span> <br />
<span>{likes}</span>
<br />
<span>{clickCount}</span>
</div>
</Profiler>
);
}
export default memo(CommentItem);

메모를 사용하지 않고 렌더링 했을때

새로운 리스트가 추가될때 이전에 있던 리스트들도 새롭게 렌더링 된다

부모에서 이벤트를 내려받아 사용시에

함수를 실행후에는 모든 리스트가 다시 렌더링 된다

Comments 자체가 리렌더 되기 때문
이럴때 useCallback을 쓴다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Comments.jsx
export default function Comments({ commentList }) {
const handleChange = useCallback(() => {
console.log('눌림');
}, []);
return (
<div>
{commentList.map((comment) => (
<CommentItem
key={comment.title}
title={comment.title}
content={comment.content}
likes={comment.likes}
onClick={handleChange}
/>
))}
</div>
);
}

handleChange에 useCallback 사용

주의 하실 점은, 함수 안에서 사용하는 상태 혹은 props 가 있다면 꼭, deps 배열안에 포함시켜야 된다는 것 입니다. 만약에 deps 배열 안에 함수에서 사용하는 값을 넣지 않게 된다면, 함수 내에서 해당 값들을 참조할때 가장 최신 값을 참조 할 것이라고 보장 할 수 없습니다. props 로 받아온 함수가 있다면, 이 또한 deps 에 넣어주어야 해요.

https://react.vlpt.us/basic/18-useCallback.html

함수가 실행후에도 기존리스트는 리렌더 되지 않는다