useMemo
를 사용하여 연산한 값 재사용하기성능 최적화를 위하여 이 전에 연산된 값을 useMemo
라는 Hook
을 사용하여 재사용.
성능을 최적화 해야 하는 상황에서 사용.
특정 값이 바꼈을때만 특정 함수 실행해서 연산을 하도록 처리.
원하는 값이 바뀌지 않았다면, 리렌더링할 때 예전에 만들어 놨던 값을 재사용할 수 있음.
Memo 는 "memoized" 를 의미하는데, 이는, 이전에 계산 한 값을 재사용한다는 의미를 가지고 있다.
useMemo
불러오기import React,{useMemo} from 'react';
countActiveUsers
라는 함수 생성 ->active
값이true
인 사용자 수를 세어서 화면에 렌더링
App.js
import React,{useRef, useState, useMemo} from 'react';
import CreateUser from './CreateUser';
import UserList from './UserList';
function countActiveUsers(users){
console.log('활성 사용자 수를 세는중...');
return users.filter(user=>user.active).length;
}
function App(){
// 상태 설정
const [inputs, setInputs] =useState({
username:'',
email:'',
});
const {username, email} = inputs; // username, email을 imputs에서 추출.
const onChange = e =>{
const {name, value}=e.target;
setInputs({
...inputs,
[name]:value // name값을 value로 덮어씌우겠다.name이 가리기고 있는것 : ( CreateUser.js의 ) name or username .
// [name]이 name이면 name을 바꾸고, [name]이 username이면 username을 바꾼다.
});
};
// 배열을 컨포넌트 상태로서 관리. users, setusers함수를 useState로 감싸주기. ->users를 return()에서 사용할 수 있게 됨.
const [users, setUsers] = useState([
{
id:1,
username: 'gyomni',
email: 'hi1@gmail.com',
active:true,
},
{
id:2,
username: 'joy',
email: 'hi2@gmail.com',
active:false,
},
{
id:3,
username: 'zoe',
email: 'hi3@gmail.com',
active:false,
}
]);
// 배열의 불변성을 지키면서 새로운 항목을 추가하는 방법 : spread 연산자(...), concat
const nextId = useRef(4); // nextId를 useRef로 관리해주는 이유는 (4)값이 바뀐다고 해서 컴포넌트가 리랜더링 뒬 필요가 없기 때문.
// 컴포넌트가 리렌더링 되도 값은 기억됨. 즉 값이 바뀐다고 해서 컴포넌트가 리렌더링 되지 않음!
const onCreate=()=>{
const user={
id:nextId.current,
username,
email,
};
setUsers([...users, user]); // 혹은 users.concat(user)로 배열 붙여주기, user에 값을 추가한다고 user.push(user); 이렇게 하면 업데이트 안됨.( slice x, sort x )-> 꼭 사용해야 한다면 배열 복사 후 사용.
// 기존의 배열 바꾸지 않고, 새로운 배열을 만들어서 거기에 변화를 주는 방식으로 구현.
// 객체와 마찬가지로 배열에서도 ...사용!
// setUsers([...users.concat(user)); -> concat사용해도됨!
setInputs({
username:'',
email:''
});
nextId.current+=1; // 여기서 리렌더링 되도 컴포넌트 값이 바뀌진 않음.
};
const onRemove = id =>{
// 각 아이템들을 파라미터에서 받아온 아이템과 비교한다 -> 만족하는 경우에 새로운 배열만들어서 넣음. 아니면 안넣음. -> setUsers배열 업데이트.
setUsers(users.filter(user=>user.id !==id)); // 제거할 때 filter사용 (불변성) 값이 false가 되면 해당 배열에서 제외되면서 삭제됨.
};
const onToggle =id =>{
// onRemove처럼 id값을 파라미터로 가져오기. users에 있는 특정 id를 선택해서 active 값을 반전시키기.
setUsers(users.map( // 불변성을 지키면서 배열 업데이트 할때 map 함수를 사용해서 구현할 수 있음.
// map함수는 특정 배열을 새로운 형태를 가진 형태로 변환시켜줄 때 사용하지만, 배열에 있는 특정 아이템만 업데이트 할 때에도 map사용가능.
user=>user.id ===id
?{ ...user, active: !user.active} // ...user -> user객체를 업데이트 할 것인데, 그 객체를 업데이트 할 때에도 불변성 지켜줘야 함. // 특정 객체를 업데이트 할 때 ...user로 기존의 user을 수정하는게 아니라, 새로운 객체를 만들어서 기존의 user가 들어있던 값을 넣어주고 특정 값을 덮어줌.
: user
));
}
const count =useMemo(()=>countActiveUsers(users),[users]);
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle}/>
<div>활성 사용자 수 : {count}</div>
</>
)
}
export default App;
CreateUser.js
import React from "react";
function CreateUser({username, email, onChange, onCreate}){ // onChange: input 값이 바뀌게 될 때 호출할 이벤트, onCreate : 버튼 눌렀을 때 새로운 항목 등록해주는 함수
return(
<div>
<input
name="username"
placeholder="계정명"
onChange={onChange}
value={username}
/>
<input
name="email"
placeholder="이메일"
onChange={onChange}
value={email}
/>
<button onClick={onCreate}>등록</button>
</div>
)
}
export default CreateUser;
UserList.js
import React, { useEffect } from "react";
function User({user, onRemove,onToggle}){ // User라는 컴포넌트 만들어줌.
const {username, email, id, active} =user; // 밑에서 user. 쓰기 번거로워서 미리 추출해서 쓰기
useEffect(()=>{ // 특정 값이 업데이트되고 난 직후에 실행이 됨.
console.log('user값이 설정됨');
console.log(user);
return()=>{
console.log('user 값이 바뀌기전');
console.log(user);
}
},[user]); // useEffect의 뎁스 배열에다가 어떤 값을 넣게 된다면 해당 값이 바뀔 때 마다 우리가 등록한 함수 호출. 해당 값이 바뀌기 직전에는 우리가 위에 설정한 clear함수 호출
// useEffect를 사용할 때는 첫번째 파라미터는 함수, 두번째 파라미터는 [] (뎁스라는 배열), return으로 특정함수 반환하게 되면 뒷정리 함수(cleanup)여서 업데이트 바로되기 직전에 호출.
// 조회하고 있는 값 있으면 []에 넣어주기.
return(
<div>
<b
style={{
color: active ?'green':'black',
cursor:'pointer',
}}
onClick={()=>onToggle(id)}
>
{username}
</b>
<span>({email})</span> {/*위에서 user추출했기때문에 user.email이 아닌 email로 썼음.*/}
<button onClick={()=>onRemove(id)}>삭제</button> {/*파라미터를 넣어주고 싶기 때문에 새로운 함수 만들기. 함수를 호출하는게 아니라 함수를 만들어서 넣어줘야함. onRemove(user.id) 에서 user쓰기 번거로우면 line4처럼 추출하면 그냥 id로 쓰면됨 */}
{/* ㄴ> 함수로 만들어주는 것이 아니라(함수를 호출하는 함수를 만들어줬음), onClick={onRemove(id)} 이렇게 하면 : 렌더링 되는 순간 onRemove(id)가 호출되버림. 그래서 컴포넌트가 실행되자마자 다 사라짐. */}
</div>
);
}
function UserList({users, onRemove,onToggle}){ // users배열을 props로 받아오기
return(
<div>
{
users.map(
(user)=>(
<User
user={user}
key ={user.id}
onRemove={onRemove}
onToggle={onToggle}
/>)
)
}
</div>
);
}
export default UserList;
📍
function countActiveUsers(users){ console.log('활성 사용자 수를 세는중...'); return users.filter(user=>user.active).length; }
const count =useMemo(()=>countActiveUsers(users),[users]);
- 첫번째 파라미터: 어떻게 연산할지를 정의하는 함수 넣기.
- 두번째 파라미터: Deps 배열 넣기
-> 배열 안에 넣은 내용이 바뀐다면 : 우리가 등록한 함수를 호출해서 값을 연산.
-> 내용이 바뀌지 않았다면 : 이전에 연산한 값을 재사용함.Users
가 호출 될 때만 호출이 되고,
그렇지 않으면 이전값을 Deps 안의 값이 바뀌여야만 값(users
)을 새로 연산해줌.
학습 : 벨로퍼트와 함께 하는 모던 리엑트