[๐ฑ๋ชฉ์ฐจ๐ฑ]
- Life Cycle
- ํจ์ํ ์ปดํฌ๋ํธ
- ํด๋์คํ ์ปดํฌ๋ํธ
- ์ค์ต
- hooks
- useMemo
- useCallback
- useReducer
- ์ปค์คํ ํ
+) ํ๊ณ
1. Life Cycle
Life cycle์ ์คํ๋ถํฐ ์ข ๋ฃ๊น์ง๋ฅผ ๋งํ๋ฉฐ ๋ชจ๋ ๋ฆฌ์กํธ ์ปดํฌ๋ํธ์ ์กด์ฌํฉ๋๋ค. Life Cycle์ ์ฉ์ด๋ฅผ ์์๋ณด์๋ฉด,
- Mount
- ์ปดํฌ๋ํธ๊ฐ ์ฒ์ ์คํ๋ ๋ ๊ทธ๊ฒ์ Mount๋ผ๊ณ ํํํฉ๋๋ค.
- DOM์ด ์์ฑ๋๊ณ ์น ๋ธ๋ผ์ฐ์ ์์ ๋ํ๋จ
- Update
- props ๋ state ๊ฐ ๋ฐ๋์์ ๋ ์ ๋ฐ์ดํธ
- Unmount
- ์ปดํฌ๋ํธ๊ฐ ํ๋ฉด์์ ์ฌ๋ผ์ง ๋
[Life Cycle ์ดํดํ๊ธฐ]
01 - 1. ํจ์ํ์ปดํฌ๋ํธ
ํจ์ํ ์ปดํฌ๋ํธ์์๋ useEffect()๋ฅผ ์ฌ์ฉํ์ฌ Mount, Update, Unmount์ ํน์ ์์
์ ์ฒ๋ฆฌํฉ๋๋ค.
๋จผ์ ๋ถ๋ชจ ์ปดํฌ๋ํธ์ ์์ ์ปดํฌ๋ํธ๋ฅผ ๋ฐ๋ก ์์ฑํด์ฃผ๊ณ
//LifeCycleFunc.js
import React, { useState } from 'react';
import LifeCycleFuncChild from './LifeCycleFuncChild';
export default function LifeCycleFunc() {
const [number, setNumber] = useState(0); //1
const changeNumber = () => {
//2
setNumber(number + 1);
};
return (
<div>
{/* 3 */}
<button onClick={changeNumber}>Plus</button>
{/* 4 */}
<LifeCycleFuncChild number={number} />
</div>
);
}
- ๋ถ๋ชจ์ปดํฌ๋ํธ๋ก useState ํ ์ ์ฌ์ฉํ์ฌ ์ด๊ธฐ๊ฐ์ด 0์ธ 'number' ์ํ๋ณ์๋ฅผ ์ด๊ธฐํํฉ๋๋ค.
- 'changeNumber' ๋ผ๋ ํจ์๋ฅผ ์ ์ํ๋๋ฐ ์ด ํจ์๋ ํธ์ถ๋ ๋ setNumber ์ํ๋ณ์๋ฅผ ์ด์ฉํ์ฌ 'number'์ ์ํ๋ฅผ 1 ์ฆ๊ฐ์ํต๋๋ค.
- ์ปดํฌ๋ํธ๋ "Plus"๋ก ๋ ์ด๋ธ๋ ๋ฒํผ์ ๋ ๋๋งํ๊ณ ๋ฒํผ์ onClick ์ด๋ฒคํธ์ changeNumber ํจ์๋ฅผ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ก ์ ๋ฌํฉ๋๋ค.
- ๋ํ number ์ํ๋ฅผ ํ๋กญ์ผ๋ก ์ ๋ฌํ์ฌ 'LifeCycleFuncChild' ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํฉ๋๋ค.
//LifeCycleFuncChild.js
import React, { useEffect } from 'react';
export default function LifeCycleFuncChild({ number }) {
useEffect(() => {
console.log('์ปดํฌ๋ํธ ๋ง์ดํธ !!');
}, []);
useEffect(() => {
console.log('์ปดํฌ๋ํธ ๋ง์ดํธ or ์
๋ฐ์ดํธ!!');
});
return (
<div>
<div>ํ์ฌ Number ๊ฐ์ {number}</div>
</div>
);
}
- ์์ ์ปดํฌ๋ํธ๋ก ๋ถ๋ชจ์ปดํฌ๋ํธ๋ก๋ถํฐ 'number'๋ผ๋ ์ด๋ฆ์ ํ๋กญ์ ๋ฐ์ต๋๋ค. ์ด ํ๋กญ์ ๋ถ๋ชจ์์ ์ ๋ฌ๋ 'number'์ํ์ ๊ฐ์ ๋ํ๋ ๋๋ค.
- ์ปดํฌ๋ํธ ๋ด๋ถ์๋ ๋๊ฐ์ 'useEffect'ํ
์ด ์์ต๋๋ค.
- ์ฒซ๋ฒ์งธ useEffect => ์์กด์ฑ ๋ฐฐ์ด์ด ๋น์ด์์ต๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ปดํฌ๋ํธ๊ฐ ์ฒ์์ผ๋ก ๋ง์ดํธ๋ ๋ ํ๋ฒ๋ง ์คํ๋ฉ๋๋ค.
- ๋๋ฒ์งธ useEffect => ์์กด์ฑ ๋ฐฐ์ด์ด ์์ต๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ๋๊ฑฐ๋ ์ ๋ฐ์ดํธ๋ ๋๋ง๋ค ์คํ๋ฉ๋๋ค.
=>Plus ๋ฒํผ ํด๋ฆญ
-> number ์ํ ์ฆ๊ฐ
->์ด ๊ฐ์ ์์ ์ปดํฌ๋ํธ์ ํ๋กญ์ผ๋ก ์ ๋ฌ
->์์ ์ปดํฌ๋ํธ๋ useEffect ํ
์ ์ฌ์ฉํ์ฌ ์ปดํฌ๋ํธ ๋ผ์ดํ์ฌ์ดํด์ ๋ณด์ฌ์ค
data ๊ฐ์ด update๋ ๋ ๋์ํ๋ useEffect()ํ
์ ์ฝ๋๋ก ๋ง๋ค์ด๋ณด์๋ฉด
import React, { useState } from 'react';
import LifeCycleFuncChild from './LifeCycleFuncChild';
export default function LifeCycleFunc() {
const [number, setNumber] = useState(0);
const [visible, setVisible] = useState(true);
const changeNumber = () => {
setNumber(number + 1);
};
const changeVisible = () => {
setVisible(!visible);
};
return (
<div>
<button onClick={changeNumber}>Plus</button>
<button onClick={changeVisible}>On/Off</button>
{visible && <LifeCycleFuncChild number={number} />}
</div>
);
}
- ๋จผ์ ๋ถ๋ชจ ์ปดํฌ๋ํธ์์ changeVisible ํจ์๋ฅผ ์์ฑํ์ฌ ๋ฒํผ ํด๋ฆญ์ visible ์ํ๋ฅผ ๋ฐ์ ์ํต๋๋ค.
- visible์ด true ์ผ๋๋ง LifeCycleFuncChild ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ๋ฉฐ, ๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ ๋ ๋๋งํ์ง ์์ต๋๋ค.
import React, { useEffect, useState } from 'react';
export default function LifeCycleFuncChild({ number }) {
const [input, setInput] = useState('');
useEffect(() => {
console.log('์ปดํฌ๋ํธ ๋ง์ดํธ !!');
}, []);
useEffect(() => {
console.log('์ปดํฌ๋ํธ ๋ง์ดํธ or ์
๋ฐ์ดํธ!!');
});
useEffect(() => {
console.log(
'์ปดํฌ๋ํธ ๋ง์ดํธ or input ์ํ๊ฐ ๋ณ๊ฒฝ๋จ์ ๋ฐ๋ผ ์ปดํฌ๋ํธ ์
๋ฐ์ดํธ'
);
}, [input]);
return (
<div>
<div>ํ์ฌ Number ๊ฐ์ {number}</div>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
/>
</div>
);
}
- input์ด๋ผ๋ ์ํ๋ณ์์ setInputํจ์๋ฅผ useState ํ ์ ์ฌ์ฉํ์ฌ ์ด๊ธฐํํฉ๋๋ค.
- ๋ง์ง๋ง useEffect ํ ์ input ์ํ๋ฅผ ์์กด์ฑ์ผ๋ก ๊ฐ์ง๊ณ ์์ด, input ์ํ๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์คํ๋ฉ๋๋ค.
01 - 2.ํด๋์คํ์ปดํฌ๋ํธ (์ฐธ๊ณ ์ฉ^^)
ํด๋์คํ ์ปดํฌ๋ํธ์์ Lifecycle ์๋ฐ๋ผ ํธ์ถ๋๋ ๋ฉ์๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- ๋ง์ดํธ(Mount)
- constructor
- render
- getDerivedStateFromProps
- componentDidMount
- ์
๋ฐ์ดํธ(Update)
- getDerivedStateFromProps
- shouldComponentUpdate
- componsntDidUpdate
- ์ธ๋ง์ดํธ(unmount)
- componentWillUnmount
//LifeCycleClass.js
import React, { Component } from 'react';
import LifeCycleClassChild from './LifeCycleClassChild';
export default class LifeCycleClass extends Component {
constructor(props) {
super(props);
this.state = {
number: 0,
visible: true,
};
}
changeNumberState = () => this.setState({ number: this.state.number + 1 });
changeVisibleState = () => this.setState({ visible: !this.state.visible });
render() {
return (
<>
<button onClick={this.changeNumberState}>PLUS</button>
<button onClick={this.changeVisibleState}>ON/OFF</button>
{this.state.visible && (
<LifeCycleClassChild number={this.state.number} />
)}
</>
);
}
}
- constructor ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์ด๊ธฐ ์ํ๋ฅผ ๊ฒฐ์ ํด์ค๋๋ค. -> number, visible ๋ ์ํ๋ณ์๋ฅผ ์ด๊ธฐํํฉ๋๋ค.
- ํ์ฌ number์ ์ํ๊ฐ์ ์์ ์ปดํฌ๋ํธ์ number ํ๋กญ์ผ๋ก ์ ๋ฌํฉ๋๋ค.
//LifeCycleClassChild.js
import React, { Component } from 'react';
export default class LifeCycleClassChild extends Component {
componentDidMount() {
console.log('์ปดํฌ๋ํธ ๋ง์ดํธ!!');
}
componentDidUpdate() {
console.log('์ปดํฌ๋ํธ ์
๋ฐ์ดํธ!');
}
componentWillUnmount() {
console.log('์ปดํฌ๋ํธ ์ธ๋ง์ดํธ!!');
}
render() {
return <div>ํ์ฌ Number ๊ฐ์ {this.props.number}์
๋๋ค.</div>;
}
}
- componentDidMount ๋ฉ์๋๋ ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ๋ ๋ ํ๋ฒ๋ง ์คํ๋ฉ๋๋ค.
- componentDidUpdate ๋ฉ์๋๋ ์ปดํฌ๋ํธ๊ฐ ์ ๋ฐ์ดํธ๋ ๋๋ง๋ค ์คํ๋ฉ๋๋ค.
- componentWillUnmount ๋ฉ์๋๋ ์ปดํฌ๋ํธ๊ฐ ์ธ๋ง์ดํธ๋ ๋ ์คํ๋ฉ๋๋ค.
[์ค์ต]
import React from 'react';
import PostItem from './PostItem';
export default function PostList() {
return (
<>
<h1 style={{ textAlign: 'center', background: 'blue', color: 'white' }}>
PostList
</h1>
<PostItem />
</>
);
}
import React, { useState, useEffect } from 'react';
export default function PostItem() {
const [posts, setPosts] = useState();
useEffect(() => {
setTimeout(() => {
setPosts(fakePosts);
}, 2000);
}, []);
const fakePosts = [
{
id: 1,
title:
'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',
body: 'quia et suscipit suscipit recusandae consequuntur expedita et cum reprehenderit molestiae ut ut quas totam nostrum rerum est autem sunt rem eveniet architecto',
},
{
id: 2,
title: 'qui est esse',
body: 'est rerum tempore vitae sequi sint nihil reprehenderit dolor beatae ea dolores neque fugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis qui aperiam non debitis possimus qui neque nisi nulla',
},
{
id: 3,
title: 'ea molestias quasi exercitationem repellat qui ipsa sit aut',
body: 'et iusto sed quo iure voluptatem occaecati omnis eligendi aut ad voluptatem doloribus vel accusantium quis pariatur molestiae porro eius odio et labore et velit aut',
},
{
id: 4,
title: 'eum et est occaecati',
body: 'ullam et saepe reiciendis voluptatem adipisci sit amet autem assumenda provident rerum culpa quis hic commodi nesciunt rem tenetur doloremque ipsam iure quis sunt voluptatem rerum illo velit',
},
{
id: 5,
title: 'nesciunt quas odio',
body: 'repudiandae veniam quaerat sunt sed alias aut fugiat sit autem sed est voluptatem omnis possimus esse voluptatibus quisest aut tenetur dolor neque',
},
{
id: 6,
title: 'dolorem eum magni eos aperiam quia',
body: 'ut aspernatur corporis harum nihil quis provident sequi mollitia nobis aliquid molestiae perspiciatis et ea nemo ab reprehenderit accusantium quas voluptate dolores velit et doloremque molestiae',
},
{
id: 7,
title: 'magnam facilis autem',
body: 'dolore placeat quibusdam ea quo vitae magni quis enim qui quis quo nemo aut saepe quidem repellat excepturi ut quia sunt ut sequi eos ea sed quas',
},
{
id: 8,
title: 'dolorem dolore est ipsam',
body: 'dignissimos aperiam dolorem qui eum facilis quibusdam animi sint suscipit qui sint possimus cum quaerat magni maiores excepturi ipsam ut commodi dolor voluptatum modi aut vitae',
},
{
id: 9,
title: 'nesciunt iure omnis dolorem tempora et accusantium',
body: 'consectetur animi nesciunt iure dolore enim quia ad veniam autem ut quam aut nobis et est aut quod aut provident voluptas autem voluptas',
},
{
id: 10,
title: 'optio molestias id quia eum',
body: 'quo et expedita modi cum officia vel magni doloribus qui repudiandae vero nisi sit quos veniam quod sed accusamus veritatis error',
},
];
return (
<div>
<div>
{posts ? (
posts.map((post) => (
<div
style={{ textAlign: 'center', background: 'skyblue' }}
key={post.id}
>
<div>
No.{post.id}-{post.title}
</div>
<br />
<div>{post.body}</div>
<hr />
</div>
))
) : (
<div style={{ textAlign: 'center' }}> Loading...</div>
)}
</div>
</div>
);
}
- useEffect ํ ์ ์ฌ์ฉํ์ฌ ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ๋ ๋ 2์ดํ์ fakePosts ๋ผ๋ ๊ฐ์์ ํฌ์คํธ๋ฐ์ดํฐ๋ฅผ setPosts ํจ์๋ฅผ ํตํด ์ค์
[์ค์ต2. ์ด์ ์ค์ต์์ ๋ก์ปฌ์ ๋ฐ์ดํฐ๋ฅผ ํธ์ถํ ๊ฒ์ jsonplaceholder ์ฌ์ดํธ๋ก ์์ฒญ ๋ณด๋ด์ ๊ฐ์ ธ์ค๊ธฐ (= axios ํจํค์ง ์ฌ์ฉํ๊ธฐ)]
npm install axios๋ก ์ค์นํ ์ฝ๋๋ฅผ ์์ ํด์ฃผ๊ฒ ์ต๋๋ค.
import React, { useState, useEffect } from 'react';
import axios from 'axios';
export default function PostItem() {
const [posts, setPosts] = useState();
useEffect(() => {
setTimeout(() => {
getPost();
}, 2000);
}, []);
const getPost = async () => {
const res = await axios.get('https://jsonplaceholder.typicode.com/posts');
setPosts(res.data);
};
return (
<div>
<div>
{posts ? (
posts.map((post) => (
<div
style={{ textAlign: 'center', background: 'skyblue' }}
key={post.id}
>
<div>
No.{post.id}-{post.title}
</div>
<br />
<div>{post.body}</div>
<hr />
</div>
))
) : (
<div style={{ textAlign: 'center' }}> Loading...</div>
)}
</div>
</div>
);
}
02. Hooks
- React ์ ์๋ก์ด ๊ธฐ๋ฅ
- ํด๋์ค ์ปดํฌ๋ํธ์์๋ง ๊ฐ๋ฅํ๋ state(์ํ๊ด๋ฆฌ)์ lifecycle(๋ผ์ดํ์ฌ์ดํด)์ด ๊ฐ๋ฅํ๋๋ก ๋๋ ๊ธฐ๋ฅ
- ํด๋์คํ ์ปดํฌ๋ํธ์์ ์ฌ์ฉํ ์ ์๋ ๊ธฐ๋ฅ์ ํจ์ํ ์ปดํฌ๋ํธ์์๋ ์ฌ์ฉํ ์ ์๋๋ก "๋์์ฑ๋ ๊ธฐ๋ฅ"
[Hooks ์ฌ์ฉ ๊ท์น]
- ์ต์์ ๋จ๊ณ์์๋ง ํธ์ถ ๊ฐ๋ฅ
- ์ต์์ ์ปดํฌ๋ํธ X
- ๋ฐ๋ณต๋ฌธ, ์กฐ๊ฑด๋ฌธ, ์ค์ฒฉ๋ ํจ์ ๋ด๋ถ์์ ํธ์ถํ๋ฉด ์ ๋๋ ๊ฒ!
- Hook์ ์ค๋ก์ง React ํจ์ํ ์ปดํฌ๋ํธ ์์์๋ง ํธ์ถ ๊ฐ๋ฅํจ.
- ์ปค์คํ ํ ์ด๋ฆ์ "use"๋ก ์์(๊ถ์ฅ์ฌํญ)
[Hooks ์ข ๋ฅ]
- useState(): ์ํ ๊ด๋ฆฌ๋ฅผ ์ํ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ํ
- useRef(): ์ฐธ์กฐ(reference)๋ฅผ ์์ฑํ๊ณ ๊ด๋ฆฌํ ์ ์๋ ํ (DOM ์ ๊ทผ, ๋ณ์ ๋ณด์กด ๋ฑ)
- useEffect(): ์ปดํฌ๋ํธ ๋ ๋๋ง ๋ ๋๋ง๋ค ํน์ ์์ ์ ์ํํ๋๋ก ์ค์ ํ ์ ์๋ ํ
- useMemo(): ๋ฉ๋ชจ์ด์ ์ด์ ์ ํตํด ํจ์์ ๋ฆฌํด ๊ฐ์ ์ฌ์ฌ์ฉํ ์ ์๊ฒ ํด์ฃผ๋ ํ
- useCallback(): ํจ์๋ฅผ ๋ฉ๋ชจ์ด์ ์ด์ ํ์ฌ ๋ถํ์ํ ๋ ๋๋ง์ ์ค์ด๊ฒ ํด์ฃผ๋ ํ
- useReducer(): ๋ณต์กํ ์ปดํฌ๋ํธ ์ํ ๋ก์ง์ ๋ฆฌ๋์ ํจ์๋ฅผ ํตํด ๊ด๋ฆฌํ ์ ์๋ ํ
02-1. useMemo()
useMemo()ํ ์ ์ฉ์ ์์๋ฅผ ๋ณด๊ฒ ์ต๋๋ค.
import React, { useState } from 'react';
export default function UseMemo() {
const [count, setCount] = useState(0);
const [input, setInput] = useState('');
//[before]
const calc = () => {
console.log('์ด์ฌํ ๊ณ์ฐ ์ค...');
for (let i = 0; i < 100000; i++) {}
return count ** 2;
};
return (
<div>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
/>
<button onClick={() => setCount(() => count + 1)}>Plus</button>
<p>count :{count} </p>
<p>calc :{calc()} </p>
</div>
);
}
- useState๋ฅผ ์ฌ์ฉํ์ฌ count ์ input ๋ ๊ฐ์ ์ํ๋ฅผ ๊ด๋ฆฌํฉ๋๋ค.
- calc ํจ์๋ ๋ฒํผ์ ํด๋ฆญํ ๋๋ง๋ค ์คํ๋๋ ํจ์๋ก ํจ์ ๋ด๋ถ์์๋ '์ด์ฌํ ๊ณ์ฐ์ค...'์ด๋ผ๋ ๋ฉ์์ง๋ฅผ ์ฝ์์ ์ถ๋ ฅ ํ ๋ฌด์๋ฏธํ ๊ณ์ฐ์ 100,000๋ฒ ๋ฐ๋ณตํ๊ณ count ์ํ์ ์ ๊ณฑ ๊ฐ์ ๋ฐํํฉ๋๋ค.
=> ์ด ์์ ๋ UseMemo๋ฅผ ์ฌ์ฉํ์ง ์์๊ธฐ ๋๋ฌธ์, calc() ํจ์๋ ๋งค ๋ ๋๋ง ๋ง๋ค ํธ์ถ๋ฉ๋๋ค. ๋ฐ๋ผ์ ๋ฒํผ์ ํด๋ฆญํ ๋๋ง๋ค '์ด์ฌํ ๊ณ์ฐ์ค...' ์ด ์ถ๋ ฅ๋๊ณ , ๋ฌด์๋ฏธํ ๊ณ์ฐ์ด ์ํ๋ฉ๋๋ค.
=> useMemo๋ฅผ ์ฌ์ฉํ๋ฉด ๊ณ์ฐ ๊ฒฐ๊ณผ๋ฅผ ์บ์ฑํ์ฌ ์ฑ๋ฅ์ ์ต์ ํํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด 'count'์ํ๊ฐ ๋ณ๊ฒฝ๋์ง ์๋ ํ, calc()ํจ์์ ๊ฒฐ๊ณผ๋ ์บ์ฑ๋์ด ์ฌ๊ณ์ฐ๋์ง ์์ต๋๋ค.
import React, { useState, useMemo } from 'react';
export default function UseMemo() {
const [count, setCount] = useState(0);
const [input, setInput] = useState('');
//[before]
// const calc = () => {
// console.log('์ด์ฌํ ๊ณ์ฐ ์ค...');
// for (let i = 0; i < 100000; i++) {}
// return count ** 2;
// };
//[after]
const calc = useMemo(() => {
for (let i = 0; i < 100000; i++) {}
return count ** 2;
}, [count]);
return (
<div>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
/>
<button onClick={() => setCount(() => count + 1)}>Plus</button>
<p>count :{count} </p>
{/* [before] */}
{/* <p>calc :{calc()} </p> */}
{/* [after] */}
<p>calc :{calc} </p>
</div>
);
}
- useMemo ํ ์ ์ฌ์ฉํ์ฌ ๊ณ์ฐ๊ฒฐ๊ณผ๋ฅผ ์บ์ฑํ ๋ ๋๋ง ์์ ๋ถํ์ํ ์ฌ๊ณ์ฐ์ ๋ฐฉ์งํฉ๋๋ค.
- useMemo๋ฅผ ์ฌ์ฉํ์ฌ calcํจ์๋ฅผ ์บ์ฑํฉ๋๋ค.
- useMemo ํ ์ ์ฌ์ฉํ์ฌ calcํจ์๋ฅผ ์ ์ํ๊ณ ๋๋ฒ์งธ ๋งค๊ฐ๋ณ์๋ก [count]๋ฅผ ์ ๋ฌํฉ๋๋ค. ์ด ๋ป์ count ์ํ๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง calcํจ์๋ฅผ ์ฌ๊ณ์ฐํ๋๋ก ํด์ค๋๋ค. ๋ค์ ๋งํด count ์ํ๊ฐ ๋ณ๊ฒฝ๋์ง ์๋ํ calc ํจ์๋ ์ด์ ์ ๊ณ์ฐ๋ ๊ฐ์ ๋ฐํํฉ๋๋ค.
- ์ฃผ์ํ ์ ์ ๋ ๋๋ง๋ถ๋ถ์์ pํ๊ทธ๋ calc ํจ์๋ฅผ ์ง์ ํธ์ถํ์ง์๊ณ {calc}๋ก ํ์ํฉ๋๋ค. ์ด๋ ๊ฒ ํ์ํ๋ฉด calcํจ์๊ฐ ์๋๋ผ ์บ์๋ ๊ฒฐ๊ณผ๊ฐ์ด ํ์๋ฉ๋๋ค.
- useMemo๋ฅผ ์ฌ์ฉํ๋ฉด calcํจ์ ์์ฒด๊ฐ ๊ฒฐ๊ณผ๊ฐ์ ๊ฐ์ง๊ณ ์์ผ๋ฏ๋ก ํจ์ ํธ์ถ ์ฐ์ฐ์ด ํ์ํ์ง ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
=> useMemo๋ฅผ ์ฌ์ฉํ์ฌ count ์ํ์ ์์กดํ๋ ๊ณ์ฐ ๊ฒฐ๊ณผ๋ฅผ ์บ์ฑํจ์ผ๋ก์จ, 'count'๊ฐ ๋ณ๊ฒฝ๋์ง ์๋ ํ ๊ณ์ฐ์ด ๋ค์ ์ํ๋์ง ์์ต๋๋ค.
02-2.useCallback()
//useCallback
// - ๋งค๋ฒ ํจ์๋ฅผ ์์ฑํ์ง ์๊ณ , ํจ์๋ฅผ ๊ธฐ์ตํด๋์๋ค๊ฐ ํ์ํ ๋๋ง๋ค ํจ์๋ฅผ ์ฌ์ฌ์ฉ
import React, { useState } from 'react';
export default function UseCallbackEx() {
const [text, setText] = useState('');
//[before]
// text ์ํ๊ฐ ์
๋ฐ์ดํธ ๋๋ฉด - ์ปดํฌ๋ํธ ๋ฆฌ๋ ๋๋ง = ์ฝ๋๋ฅผ ๋ค์ ์ฝ์
// = onChangeText ํจ์๋ ๋ค์ ์์ฑ (js func๋ object type์ด๋ผ ์ฃผ์๊ฐ์ด ๋ณ๊ฒฝ)
// => ๋ถํ์ํ ์์
!
const onChangeText = (e) => {
setText(e.target.value);
};
return (
<div>
<h1>UseCallbackEx</h1>
<input type="text" onChange={onChangeText} />
<div>์์ฑํ ๊ฐ:{text || '์์'}</div>
</div>
);
}
- ์ด ์ฝ๋์์ onChangeText ํจ์๊ฐ ๋งค ๋ ๋๋ง ๋ง๋ค ์๋ก ์์ฑ๋ฉ๋๋ค. JS ํจ์๋ ๊ฐ์ฒด์ด๋ฏ๋ก ๊ฐ ํจ์๋ ๊ณ ์ ํ ์ฃผ์๋ฅผ ๊ฐ๋๋ฐ, text ์ํ๊ฐ ์ ๋ฐ์ดํธ๋ ๋๋ง๋ค ํจ์๊ฐ ์๋ก ์์ฑ๋๊ณ , ํจ์๊ฐ ๋ณ๊ฒฝ๋๋ฉด ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง๋ฉ๋๋ค.
- ์ด๋ ๊ฒ ๋งค ๋ ๋๋ง๋ง๋ค ํจ์๊ฐ ์ฌ์์ฑ๋๋๊ฒ์ ๋ถํ์ํ ์์ ์ด๋ฏ๋ก ์ฑ๋ฅ ์ ํ๋ฅผ ์ด๋ํ ์ ์์ต๋๋ค.
=> useCallback ํ ์ ์ฌ์ฉํ๋ฉด ํจ์๋ฅผ ๋ฉ๋ชจ์ด์ ์ด์ ํ๊ณ ํ์ํ ๋๋ง๋ค ์ด์ ์ ์์ฑํ ํจ์๋ฅผ ์ฌ์ฌ์ฉํ ์ ์์ผ๋ฏ๋ก ์ฑ๋ฅ์ ์ต์ ํํ ์ ์์ต๋๋ค.
//useCallback
// - ๋งค๋ฒ ํจ์๋ฅผ ์์ฑํ์ง ์๊ณ , ํจ์๋ฅผ ๊ธฐ์ตํด๋์๋ค๊ฐ ํ์ํ ๋๋ง๋ค ํจ์๋ฅผ ์ฌ์ฌ์ฉ
import React, { useCallback, useState } from 'react';
export default function UseCallbackEx() {
const [text, setText] = useState('');
//[before]
// text ์ํ๊ฐ ์
๋ฐ์ดํธ ๋๋ฉด - ์ปดํฌ๋ํธ ๋ฆฌ๋ ๋๋ง = ์ฝ๋๋ฅผ ๋ค์ ์ฝ์
// = onChangeText ํจ์๋ ๋ค์ ์์ฑ (js func๋ object type์ด๋ผ ์ฃผ์๊ฐ์ด ๋ณ๊ฒฝ)
// => ๋ถํ์ํ ์์
!
// const onChangeText = (e) => {
// setText(e.target.value);
// }
//[after]
//์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง ๋์ด๋, ์์กด์ฑ ๋ฐฐ์ด์ ์๋ ๊ฐ์ด ๋ฐ๋์ง ์๋ ํ ๊ธฐ์กด ํจ์๋ฅผ ๊ณ์ ์ฌ์ฉ
const onChangeText = useCallback((e) => {
setText(e.target.value);
}, []);
return (
<div>
<h1>UseCallbackEx</h1>
<input type="text" onChange={onChangeText} />
<div>์์ฑํ ๊ฐ:{text || '์์'}</div>
</div>
);
}
- ์ ์์ ๋ UseCallback ์ ์ฌ์ฉํ์ฌ onChangeText ํจ์๋ฅผ ๋ฉ๋ชจ์ด์ ์ด์ ํฉ๋๋ค.
- onChangeText ํจ์์ ๋๋ฒ์งธ ๋งค๊ฐ๋ณ์๋ก ๋น ๋ฐฐ์ด('[]')์ ์ ๋ฌํ์์ผ๋ฏ๋ก ํจ์ ๋ด๋ถ์์ ์ฌ์ฉํ๋ ์์กด์ฑ์ด ์์์ ์๋ฏธํ๋ฉฐ, ๋ฐ๋ผ์ onChangeText ํจ์๋ ์ปดํฌ๋ํธ๊ฐ ์ฒ์ ๋ ๋๋ง๋ ๋ ํ๋ฒ๋ง ์์ฑ๋๊ณ ๋ฉ๋ชจ์ด์ ์ด์ ๋ฉ๋๋ค.
- ์์กด์ฑ ๋ฐฐ์ด์ ๊ฐ์ด ์์ ๊ฒฝ์ฐ, ๊ทธ ๊ฐ์ด ๋ณ๊ฒฝ๋ ๋๋ง๋ค ํจ์๊ฐ ์ฌ์์ฑ๋ฉ๋๋ค.
import React, { useState } from 'react';
import axios from 'axios';
export default function UseCallbackEx2({ postId }) {
//props ๋ฐ๋ก ๊ตฌ์กฐ๋ถํด
const [post, setPost] = useState({});
//[before]
const getPost = async () => {
console.log('data fetching ...');
const res = await axios.get(
`https://jsonplaceholder.typicode.com/posts/${postId}`
);
setPost(res.data);
};
//useEffect ์์กด์ฑ ๋ฐฐ์ด์ 'ํจ์'
//์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง -> ํจ์ ์ฌ์์ฑ(์ฃผ์๊ฐ ๋ณ๊ฒฝ) -> getPost ์ฌํธ์ถ
useEffect(() => {
getPost();
}, []);
// ์ฃผ์๊ฐ์ด ๋ฐ๋ ๋๋ง๋ค ๊ณ์ ํจ์ ํธ์ถ๋จ
return (
<div>
<h1>UseCallback Ex2</h1>
{post.id ? post.title : '๋ก๋ฉ์ค ...'}
</div>
);
}
- ์์ ์ฝ๋์์ getPost ํจ์๋ ์ธ๋ถ API๋ก๋ถํฐ ๊ฒ์๋ฌผ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๋น๋๊ธฐ ํจ์์ ๋๋ค. axios๋ฅผ ์ฌ์ฉํ์ฌ ํน์ postId์ ํด๋นํ๋ ๊ฒ์๋ฌผ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ ๋ค, setPostํจ์๋ฅผ ์ฌ์ฉํ์ฌ post ์ ์ํ๋ฅผ ์ ๋ฐ์ดํธ ํฉ๋๋ค.
- useEffect ํ ์ ์ฌ์ฉํ์ฌ ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง๋ ๋ getPost ํจ์๋ฅผ ํธ์ถ์์ผ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
- getPost ํจ์์ ์ฃผ์๊ฐ์ด ๋ณ๊ฒฝ๋ ๋๋ง useEffect๊ฐ ์คํ๋๋๋ก ์ค์ ํ๋ฏ๋ก getPost ํจ์์ ์ฃผ์๊ฐ ๋ณ๊ฒฝ๋์ง ์๋ํ, useEffect ๋ด๋ถ์ ํจ์๋ ์ฌ์์ฑ๋์ง ์์ต๋๋ค.
import React, { useState, useCallback, useEffect } from 'react';
import axios from 'axios';
export default function UseCallbackEx2({ postId }) {
//props ๋ฐ๋ก ๊ตฌ์กฐ๋ถํด
const [post, setPost] = useState({});
//[before]
// const getPost = async () => {
// console.log('data fetching ...');
// const res = await axios.get(
// `https://jsonplaceholder.typicode.com/posts/${postId}`
// );
// setPost(res.data);
// };
//[after]
const getPost = useCallback(async () => {
console.log('data fetching ...');
const res = await axios.get(
`https://jsonplaceholder.typicode.com/posts/${postId}`
);
setPost(res.data);
}, [postId]);
//useEffect ์์กด์ฑ ๋ฐฐ์ด์ 'ํจ์'
//์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง -> ํจ์ ์ฌ์์ฑ(์ฃผ์๊ฐ ๋ณ๊ฒฝ) -> getPost ์ฌํธ์ถ
useEffect(() => {
getPost();
}, [getPost]);
// ์ฃผ์๊ฐ์ด ๋ฐ๋ ๋๋ง๋ค ๊ณ์ ํจ์ ํธ์ถ๋จ
return (
<div>
<h1>UseCallback Ex2</h1>
{post.id ? post.title : '๋ก๋ฉ์ค ...'}
</div>
);
}
- ์์ ์ฝ๋๋ useCallback์ ์ฌ์ฉํ์ฌ ํจ์๋ฅผ ๋ฉ๋ชจ์ด์ ์ด์ ํ๊ณ , useEffect ์ ์์กด์ฑ๋ฐฐ์ด์ getPost๋ฅผ ์ฌ์ฉํ์ฌ ํจ์๊ฐ ์ฃผ์๊ฐ์ด ๋ณ๊ฒฝ๋ ๋๋ง ํธ์ถ๋๋๋ก ๋ณ๊ฒฝํ ์ปดํฌ๋ํธ์ ๋๋ค.
- useCallback์ ์ฌ์ฉํ์ฌ getPost ํจ์๋ฅผ ๋ฉ๋ชจ์ด์ ์ด์ ํ๋๋ฐ ๋๋ฒ์งธ ๋งค๊ฐ๋ณ์๋ก [postId]๋ฅผ ์ ๋ฌํฉ๋๋ค. ์ด๊ฒ์ ํจ์ ๋ด๋ถ์์ ์ฌ์ฉํ๋ postId ๋ณ์๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง getPost ํจ์๊ฐ ์ฌ์์ฑ๋๋๋กํฉ๋๋ค. ๋ณ์๊ฐ ๋ณ๊ฒฝ๋์ง ์๋๋ค๋ฉด ์ฒ์ ๋ ๋๋ง๋ ๋ ํ ๋ฒ๋ง ์์ฑ๋๊ณ ๋ฉ๋ชจ์ด์ ์ด์ ๋ฉ๋๋ค.
02-3.useReducer()
import React, { useReducer } from 'react';
const initState = { value: 0 }; //์ด๊ธฐ ์ํ ๊ฐ
const reducer = (prevState, action) => {
//action:{type:xxx}
switch (action.type) {
case 'INCREMENT':
return { value: prevState.value + 1 };
case 'DECREMENT':
return { value: prevState.value - 1 };
case 'RESET':
return initState;
default:
return { value: prevState.value };
}
};
export default function UseReducer() {
//reducer : state๋ฅผ ์
๋ฐ์ดํธํ๋ ํจ์
//dispatch : ์ก์
state๊ฐ ์ด๋ป๊ฒ ๋ณ๊ฒฝ๋์ด์ผ ํ๋์ง์ ๋ํ ํํธ)์ ๋ฐ์์ํค๋ ํจ์
//state: ํ์ฌ ์ํ
const [state, dispatch] = useReducer(reducer, initState);
//dispatch -> ์ก์
์ ๋ฐ์์ํค๋ ํจ์
const increment = () => dispatch({ type: 'INCREMENT' });
const decrement = () => dispatch({ type: 'DECREMENT' });
const reset = () => dispatch({ type: 'RESET' });
return (
<div>
<h1>UseReducer_EX</h1>
<h2>{state.value}</h2>
<button onClick={increment}>Plus</button>
<button onClick={decrement}>Minus</button>
<button onClick={reset}>Reset</button>
</div>
);
}
์ด ์ฝ๋๋ useReducer ํ
์ ์ฌ์ฉํ์ฌ ์ํ ๊ด๋ฆฌ๋ฅผ ํ๋ ์์ ์
๋๋ค.
useReducerํ
์ ์ํ์ ๊ด๋ จ๋ ๋ก์ง์ ๋ถ๋ฆฌํ๊ณ ์ก์
์ ํตํด ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ ๋ฐฉ์์
๋๋ค.
- ์ฐ์ , ์ฒซ๋ฒ์งธ๋ก ์ด๊ธฐ์ํ ('initState')๋ฅผ ์ ์ํฉ๋๋ค.
- initState ๊ฐ์ฒด๋ ์ด๊ธฐ ์ํ ๊ฐ์ ๊ฐ์ง๋๋ค. ์ด ์์ ๋ value๋ผ๋ ์์ฑ์ ๊ฐ์ง ๊ฐ์ฒด๋ก ์ด๊ธฐ๊ฐ์ 0์ ๋๋ค.
- ๋๋ฒ์งธ๋ก ๋ฆฌ๋์ ํจ์๋ฅผ ์ ์ํฉ๋๋ค.
- ๋ฆฌ๋์ ํจ์(Reducer)๋ ํ์ฌ ์ํ(prevState)์ ์ก์ ๊ฐ์ฒด๋ฅผ ๋ฐ์์ ์๋ก์ด ์ํ๋ฅผ ๋ฐํํ๋ ํจ์์ ๋๋ค.
- ์ก์ ๊ฐ์ฒด์ type ์์ฑ์ ๋ฐ๋ผ ๋ค์ํ ๋์์ ์ํํฉ๋๋ค.
- useReducer ํจ์๋ฅผ ํธ์ถํ์ฌ ํ์ฌ ์ํ(state)์ ์ํ๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ dispatch ํจ์๋ฅผ ๋ฐํํฉ๋๋ค.
02-4. ์ปค์คํ ํ ๋ง๋ค๊ธฐ
//useToggle.js
import { useState } from 'react';
export default function useToggle(initValue = false) {
//value : ํ ๊ธ์ ์ํ
//setValue : ํ ๊ธ ์ํ๋ฅผ ๋ณํ ์ํค๋ setter
const [value, setValue] = useState(initValue);
//ํ ๊ธ ์ํ๋ฅผ ์ ํ (-> ์ค์์นญ)
const toggleValue = () => {
setValue(!value);
};
return [value, toggleValue];
}
import React from 'react';
import useToggle from '../hooks/useToggle';
export default function Faq() {
const [isFaqOpen, setIsFaqOpen] = useToggle();
return (
<div>
<h1>custom hook (useToggle) EX</h1>
<div onClick={setIsFaqOpen} style={{ cursor: 'pointer' }}>
ํ!!!
</div>
{isFaqOpen && <div>ํ ํผํ์ง๋กฑ.</div>}
</div>
);
}
์ ์์ ๋ ์ปค์คํ ํ ์ ์ฌ์ฉํ์ฌ ๊ฐ๋จํ ํ ๊ธ ๋์์ ๊ตฌํํ์ต๋๋ค.
- Faq ์ปดํฌ๋ํธ -> ์ด ์ปดํฌ๋ํธ๋ ํ ๊ธ ๊ธฐ๋ฅ์ ๊ตฌํํ๊ณ ์ฌ์ฉํฉ๋๋ค. useToggle ์ปค์คํ ํ ์ ์ฌ์ฉํ์ฌ isFaqOpen ์ํ์ setIsFapOpen ํ ๊ธ ํจ์๋ฅผ ์์ฑํฉ๋๋ค.
- isFaqOpen ์ํ๊ฐ true ์ธ ๊ฒฝ์ฐ์๋ง <div>์์๊ฐ ๋ ๋๋ง๋๋๋ก ์ค์ ํ์ต๋๋ค.
useToggle ์ปค์คํ
ํ
- useToggle ํจ์๋ ์ด๊ธฐ๊ฐ(initValue)์ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์ผ๋ฉฐ, ๊ธฐ๋ณธ๊ฐ์ false ์ ๋๋ค.
- ๋ด๋ถ์ ์ผ๋ก useState ํ ์ ์ฌ์ฉํ์ฌ 'value'์ 'setValue'์ํ๋ฅผ ์์ฑํฉ๋๋ค.
- useToggle ํจ์๋ [value,toggleValue]ํํ์ ๋ฐฐ์ด์ ๋ฐํํฉ๋๋ค. ํ ๊ธ ์ํ์ ํ ๊ธ ํจ์๋ฅผ ์์๋๋ก ๊ฐ์ง๊ณ ์์ต๋๋ค.
์ ๋ฆฌ : ํด๋ฆญ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด setIsFaqOpen ํจ์ ํธ์ถ -> useToggle ํ
์ toggleValue ํจ์ ์คํ ->value์ ์ํ ๋ณํ ->isFaqOpen ์ํ ๋ณํ -> ์กฐ๊ฑด๋ถ์ ๋ฐ๋ผ div ํ๊ทธ ๋ณด์ด๊ธฐ/ ์๋ณด์ด๊ธฐ ์์๋ก ์งํ๋๋ ๋ฏ ์ถ๋ค.
+ํ๊ณ )
useReducer ํ ์์ ์ {state}๊ฐ ์๋๊ณ {state.value}๋ฅผ ์ธ๊น ๊ถ๊ธํ์๋๋ฐ useReducer๋ฅผ ํตํด์ ๋ฐํ๋ ์ํ ๊ฐ์ฒด์ธ 'state'๊ฐ ๊ฐ์ฒด ํํ์ธ {value: 0}์ ๊ฐ์ด ๊ตฌ์ฑ๋์ด ์๊ธฐ ๋๋ฌธ์ด๋ผ๊ณ ํ๋ค. ๋ง์ฝ ์ด๊ธฐ์ํ๊ฐ ๊ฐ์ฒด๊ฐ ์๋ ์ซ์, ๋ฌธ์์ด์ด๋ผ๋ฉด {state}๋ก ์ฌ์ฉํด๋ ๋๋๊ตฐ.. ํ๋๋ฐ ์ผ๋ฐ์ ์ผ๋ก useReducer๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฐ์ฒด ํํ์ ์ํ๋ฅผ ๋ค๋ฃฌ๋ค๊ณ ํ๋ค. ๋ ์ปค์คํ ํ ๋ง๋๋ ๊ณผ์ ์์ ๋ฐฐ์ด๊ตฌ์กฐ๋ถํด๋ฅผ ์ํ๋ฉด ์ด๋ป๊ฒ ๊ฐ์ ธ์์ผํ ๊น ํ๋๋ฐ
const toggleState = useToggle();
const isFaqOpen = toggleState[0];
const setIsFaqOpen = toggleState[1];
์ด๋ฐ์์ผ๋ก ๋งค์ฐ ์ง์ ๋ถํ๋ค.. ๊ทธ๋ฅ ๋ฐฐ์ด๊ตฌ์กฐ๋ถํด ํด์ฃผ์ ^^ ๊ตฌ์กฐ๋ถํด ํด์ฃผ๋๊ฒ ๊ฐ๋ ์ฑ์ด ๋ ์ข๊ณ ๋ณ์์ ์ญํ ์ด ๋ช ํํ ๋ณด์ธ๋ค. ์ ๋ฒ ํ๋ก์ ํธ ํ๋ฉด์ ๊ตฌ์กฐ๋ถํด๋ฅผ ์ด๋ค๊ฑด ํ๊ณ ์ด๋ค๊ฑด ์ํ๊ณ ์ด๋๋์ ํ ๊ฑฐ๋ฉด ๋ค ํ๊ณ ํ์คํ๊ฒ ํด์ผ๊ฒ ๋ค. ๋ด๊ฐ ๊ตฌ์กฐ๋ถํด๋ฅผ ํด๋์ผ๋ฉด ๋ค๋ฅธ ํ์์ด ๋ชป์์๋ณผ๊น๋ด ์ํด๋๊ธดํ๋๋ฐ ์ด๋ ๊ฒ ๋ณ์๋ช ์ ์ ์ ํด๋์ผ๋ฉด ๊ตฌ์กฐ๋ถํด๋ฅผ ํ์ ๋ ๋ช ํํ ๋ณด์ด๊ณ ์ฝ๋๋ ๊ฐ๊ฒฐํด์ ธ์ ๊ฐ๋ ์ฑ์ด ๋ ์ข์์ง๋๊ฒ ๊ฐ๋ค.
'SeSAC' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
2์ฐจ ํ๋ก์ ํธ ํ๊ณ [์ด์ค์ฌ์ด] (4) | 2023.11.20 |
---|---|
[SeSACXCodingOn] ์นํ์คํ๊ณผ์ 14W_02 : React hook form (4) | 2023.10.16 |
[SeSACXCodingOn] ์นํ์คํ๊ณผ์ 14W_01 : ๋ฆฌ์กํธ ๋ผ์ฐํฐ (4) | 2023.10.16 |
[SeSACXCodingOn] ์นํ์คํ๊ณผ์ 13W_02 : ๋ฆฌ์กํธ (3) (0) | 2023.10.11 |