이번 주 배울 것 : 함수형 컴포넌트 hooks 5가지 (총 10가지 존재)
useMemo
useCallback
useReducer
useContext
useEffect
이전에 함수의 state를 선언할 때 쓰고있던 useState도 hooks API 중 하나였다.
이전 내용 복씁
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>REACT TIME</title>
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
class CounterClass extends React.Component{
// constructor(props){
// super(props)
// this.state={
// number:0
// }
// } // babel 덕분에 위의 구문이 아래처럼 간단해진다 !
state={
number:0
}
render(){
return(
<div>
<h1>{this.state.number}</h1>
<button>+1</button>
<button>-1</button>
</div>
)
}
}
ReactDOM.render(
<CounterClass/>,
document.querySelector('#root')
)
</script>
</body>
</html>
원래 위의 주석처리된 코드이지만 babel 덕분에 아래 stat={} 요렇게 간단히 가능했다.
이제 버튼 클릭해서 number가 변하게 만들기 !!
state={
number:0
}
handleClick1 = ()=>{
this.setState({
number: this.state.number +=1
})
}
handleClick2 =()=>{
this.setState({
number: this.state.number -=1
})
}
render(){
return(
<div>
<h1>{this.state.number}</h1>
<button onClick={this.handleClick1}>+1</button>
<button onClick={this.handleClick2}>-1</button>
</div>
)
}
}
위의 내용을 함수로 만들기
const CounterFn = ()=>{
const [number, setNumber] = React.useState(0)
const up = () =>{
setNumber(number+1)
}
const down = () =>{
setNumber(number-1)
}
return(
<div>
<h1>{number}</h1>
<button onClick={up}>+1</button>
<button onClick={down}>-1</button>
</div>
)
}
ReactDOM.render(
<CounterFn/>,
document.querySelector('#root')
)
userState(0)요 부분이 왜 이렇게 낯설지,,,,,,,,,,,,,
함수형은 setState가 바뀌면 전체가 다시 render 되고 클래스는 render() 부분만 render가 되어서 함수형은 많은 연산을 포함하게 되면 느릴수도 있다. (성능저하) ---> 요거를 커버하는 기술을 오늘 배울 예정
클래스 렌더되는 시점 쳌
class Counterclass extends React.Component{
constructor(props){ // CounterFn 가 실행되면 constructor 가 실행됨
super(props)
this.state={ // number : 0 세팅이 됨
number:0
}
console.log('counterclass')
} // babel 덕분에 위의 구문이 아래처럼 간단해진다 !
handleClick1 = ()=>{
this.setState({
number: this.state.number +=1
})
}
handleClick2 =()=>{
this.setState({
number: this.state.number -=1
})
}
render(){
return(
<div>
{console.log('rendering')}
<h1>{this.state.number}</h1>
<button onClick={this.handleClick1}>+1</button>
<button onClick={this.handleClick2}>-1</button>
</div>
)
}
}
const CounterF
setState 가 되어 render될 때 render안의 구문만 다시 실행된다 ---> 'rendering' 만 계속 찍힘
함수형 렌더되는 시점 쳌
const CounterFn = ()=>{
const [number, setNumber] = React.useState(0)
const up = () =>{
setNumber(number+1)
}
const down = () =>{
setNumber(number-1)
}
console.log('CounterFn')
return(
<div>
{console.log('rendering')}
<h1>{number}</h1>
<button onClick={up}>+1</button>
<button onClick={down}>-1</button>
</div>
)
}
함수형은 setState될 때 CounterFn 안의 전체 코드를 render 한다 ! -> 'counterFn', 'rendering' 둘 다 찍힘
함수형을 아래처럼 바꿔보기
const CounterFn = ()=>{
const [number, setNumber] = React.useState(0)
const up = () =>{
setNumber((prevNumber)=>prevNumber+1)
}
const down = () =>{
setNumber((prevNumber)=>prevNumber-1)
}
console.log('CounterFn')
return(
<div>
{console.log('rendering')}
<h1>{number}</h1>
<button onClick={up}>+1</button>
<button onClick={down}>-1</button>
</div>
)
}
prevNumber 인자값은 아무거나 쓸 수 있다 - 이전에 대한 상태값을 인자값으로 받을 때의 코드 !
위의 코드와 똑같이 +1 -1 이 된다.
Memoization
Momoization 메모이제이션
(memoization)은 컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술이다. 동적 계획법의 핵심이 되는 기술이다.
메모이제이션을 이해해야 use를 알 수 있다!
피..피보나치 수열... ?
첫 번째 , 두 번째 항은 무조건 1 ! -> 이후 더하기
아래 설명을 보니 이해가 되었다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Memoization</title>
<script type="text/javascript">
function fibo(n){
if(n==1)return 1;
if(n==2)return 1;
return fibo(n-2) + fibo(n-1);
}
</script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
</script>
</body>
</html>
---> 재귀함수로 사용한 피보나치 수열
위처럼 계속 콜백 지옥..
문제점 : 수가 조금이라도 크면 (ex.50) 연산이 느어무 오래걸림
n 이 4인 경우부터 계속 콜백함수로 불려와서 더해진다.
--> 메모이제이션 : 한 번 연산했던 이전의 값을 저장해 두었다가 재사용 할 수 있도록 만듬
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Memoization</title>
<script type="text/javascript">
let memo = {} // memo 라는 공간 설정
function fibo(n){
let result;
if(n in memo){ // 계산한 적이 있다면
result = memo[n]
}else{ // 아직 계산한 적이 없다면
if(n==1 || n==2){
result = 1
}else{
result= fibo(n-2) + fibo(n-1)
}
// 새로 계산한 것을 memo에 넣기
memo[n] = result;
}
return result;
}
</script>
</head>
<body>
</body>
</html>
-> 큰 수도 연산이 잘 된다 !
Memoization 만들어보기 !
1. 웹팩 기본 설정하기
https://blckchainetc.tistory.com/255
2. memo 폴더 - memo.jsx 파일 생성 / 코드 작성 / App과 연결
memo.jsx
import React, {useState} from 'react';
const Memo = () =>{
// 두 개의 상태값 : 1. input 1. 내용 저장
const [username,setUsername] = useState('')
const [list,setList] = useState([])
return(
<>
<h2>회원리스트(0)</h2>
<form>
<input type="text" name="username" />
<button type="submit">
추가
</button>
</form>
<ol>
<li>emily1</li>
<li>emily2</li>
<li>emily3</li>
</ol>
</>
)
}
export default Memo
App.jsx
import React from 'react';
import Memo from './memo/memo';
const App = () => {
return(
<>
<Memo />
</>
)
}
export default App
3. 내용 쓰고 추가 누르면 리스트 추가 하도록 만들기
memo.jsx
import React, {useState} from 'react';
const Memo = () =>{
// 두 개의 상태값 : 1. input 1. 내용 저장
const [username,setUsername] = useState('')
const [list,setList] = useState([])
const change = (e) =>{
let {value} = {...e.target}
setUsername(value)
}
return(
<>
<h2>회원리스트(0)</h2>
<form>
<input
type="text"
name="username"
onChange={change}
/>
<button type="submit">
추가
</button>
</form>
<ol>
<li>emily1</li>
<li>emily2</li>
<li>emily3</li>
</ol>
</>
)
}
export default Memo
state 바뀌는 것 확인 ↓↓
4. submit 함수 추가 / 작동 확인
memo.jsx
import React, {useState} from 'react';
const Memo = () =>{
// 두 개의 상태값 : 1. input 1. 내용 저장
const [username,setUsername] = useState('')
const [list,setList] = useState([])
const change = (e) =>{
let {value} = {...e.target}
setUsername(value)
}
const submit = (e) =>{
e.preventDefault()
console.log('submit 잘 동작함니다.')
}
return(
<>
<h2>회원리스트(0)</h2>
<form onSubmit={submit}>
<input
type="text"
name="username"
value={username}
onChange={change}
/>
<button type="submit">
추가
</button>
</form>
<ol>
<li>emily1</li>
<li>emily2</li>
<li>emily3</li>
</ol>
</>
)
}
export default Memo
얕은 복사하기
slice() or [...list] 로 !
import React, {useState} from 'react';
const Memo = () =>{
// 두 개의 상태값 : 1. input 1. 내용 저장
const [username,setUsername] = useState('')
const [list,setList] = useState([])
const change = (e) =>{
let {value} = {...e.target}
setUsername(value)
}
const submit = (e) =>{
e.preventDefault()
let newList = [...list] //==list.slice()
newList.push(username)
setList(newList)
setUsername('')
}
return(
<>
<h2>회원리스트(0)</h2>
<form onSubmit={submit}>
<input
type="text"
name="username"
value={username}
onChange={change}
/>
<button type="submit">
추가
</button>
</form>
<ol>
<li>emily1</li>
<li>emily2</li>
<li>emily3</li>
</ol>
</>
)
}
export default Memo
-> 브라우저에서 input box에 입력하고 '추가'누르면 input box clear
***
** 배열에 담고만 있어도 list 형태로 jsx 가 알아서 return 해준다 !
=> 그럼 코드로 만들 때 값을 배열로 만들어서 return 만 해주면 됨
memo.jsx
import React, {useState} from 'react';
const Memo = () =>{
// 두 개의 상태값 : 1. input 1. 내용 저장
const [username,setUsername] = useState('')
const [list,setList] = useState([])
const change = (e) =>{
let {value} = {...e.target}
setUsername(value)
}
const submit = (e) =>{
e.preventDefault()
let newList = [...list] //==list.slice()
newList.push(username)
setList(newList)
setUsername('')
}
const renderList=()=>{
let newArr = [];
for (let i=0; i<list.length; i++){
newArr.push(<li key={i}>{list[i]}</li>)
}
return newArr
}
return(
<>
<h2>회원리스트(0)</h2>
<form onSubmit={submit}>
<input
type="text"
name="username"
value={username}
onChange={change}
/>
<button type="submit">
추가
</button>
</form>
<ol>
{renderList()}
</ol>
</>
)
}
export default Memo
내용 추가하면 바로바로 나온다 !
위의 코드
1. for문
let newArr = [];
for (let i=0; i<list.length; i++){
newArr.push(<li key={i}>{list[i]}</li>)
}
return newArr
2. map
let newArr = list.map((v,k)=>{
return <li key={k}>{v}</li>
})
return newArr
3. map 바로 return
return (
list.map((v, k) => {
return <li key={k}>{v}</li>
})
)
여기까지 전체 코드 및 설명
import React, { useState } from 'react';
const Memo = () => {
// 두 개의 상태값 : 1. input 1. 내용 저장
const [username, setUsername] = useState('')
const [list, setList] = useState([])
const change = (e) => {
let { value } = { ...e.target } // 2. value 에 inputbox의 입력한 내용 담기
setUsername(value) // 3. state에 저장
}
const submit = (e) => {
e.preventDefault()
let newList = [...list] // (2) newList 에 state의 현재 list 값 담기
newList.push(username) // (3) newList 에 state의 username 담기 (input box에서 입력한 값)
setList(newList) // (4) newList 수정하기
setUsername('') // (5) state.username의 값 (inputbox에 담은값) reset하기
}
const renderList = () => {
let newArr = list.map((v,k)=>{ // 2222 list의 각 원소에 <li>요소, key값 추가한 새로운 배열 담기
return <li key={k}>{v}</li>
})
return newArr // 333 해당 새로운 배열 newArr return
}
return (
<>
<h2>회원리스트(0)</h2>
<form onSubmit={submit}> {/* (1) submit 클릭 */}
<input
type="text"
name="username"
value={username} // 4. state에 저장한 value으로 변경
onChange={change} // 1. inputbox에 회원명 입력
/>
<button type="submit">
추가
</button>
</form>
<ol>
{renderList()} {/* 1111) renderList함수 실행 */}
</ol>
</>
)
}
export default Memo
5. 회원리스트(0) -> 요 숫자 카운팅 함수 만들기
const count = () =>{
return list.length
}
return (
<>
<h2>회원리스트({count})</h2>
<form onSubmit={submit}> {/* (1) submit 클릭 */}
use memo
6. 이제 memorization !
위의 코드 불필요한 부분 -> 모든 함수가 사용될 때 rerender되는 부분이 비효율적
특히 change 부분 변화가 있을 때마다 inputbox에 글을 한 글자라도 쓸 때마다 render 됨
memo.jx
import React, { useState, useMemo } from 'react';
const count = () =>{
return list.length // 삭제
}
const userCount = useMemo(()=>{
console.log(33)
return list.length
}, [list.length])
return (
<>
<h2>회원리스트({userCount})</h2>
<form onSubmit={submit}>
<input
type="text"
언제 console.log가 찍히는지 보기 !
-> 글을 쓰는 중에는 안찍히고 '추가' 버튼을 눌렀을 떄 console찍힘
위의 코드 설명
const userCount = useMemo(()=>{ //useMemo 첫번째 인자값 : 함수 , 두 번째 인자값 : 배열 (상태값)
console.log(33)
return list.length // 최종적으로 list.length를 userCount에 담기
}, [list.length]) // list의 길이가 변할 떄만 ! return 하도록
// 내가 userCount에 넣은 값과 상태값(두 번째 인자값인) list.length와 다를 경우에만 ~ 요 매서드를 실행하겠다.
return (
<>
<h2>회원리스트({userCount})</h2>
<form onSubmit={submit}>
메모리에 저장하는 방법 ---> 메모리도 제한이 있기 떄문에 막 쓰면 안됨 ! ( = 자원의 교환 )
ex) 함수 컴포넌트 쓰니깐 다 useMemo 써서 저장해야징~ -> X
클래스의 경우 (pure Component == 함수의 useMemo)
useCallback
useCallback 과 useMemo의 차이 :
useCallback 함수를 memoiztion 할 때
useMemo는 변수의 값을 memorzation 사용할 때 (return 값이 있는 것들)
7. useCallback 가져오기 (useMemo처럼!)
import React, { useState, useMemo, useCallback } from 'react';
8. chang 함수를 useCallback으로 바꿔보기
const change = (e) => {
let { value } = { ...e.target } /
setUsername(value)
}
const change = useCallback((e)=>{ // Memo 함수 Component가 처음 실행될 때 heaq에 담아짐
let {value} = {...e.target}
setUsername(value)
},[])
요 것도 너무 많이 쓰면 성능 저하가 올 수 있다. sytyle component를 만들 때 쓰는게 좋을 듯 ! style 바뀔 때
[] 빈 배열 - 최초에 component를 실행할 때 change 함수를 생성하겠다. 라는 뜼
submit 변경
// const submit = (e) => {
// e.preventDefault()
// let newList = [...list]
// newList.push(username)
// setList(newList)
// setUsername('')
// }
const submit = useCallback((e)=>{
e.preventDefault()
let newList = [...list]
newList.push(username)
setList(newList)
setUsername('')
},[list,userName]) // submit 함수는 list와 usename 두 가지를 변경 함
함수 submit에서 바꾸는 내용은 list, username이 있다. []안에 두개 다 쓰기
list와 username 상태가 바뀔 때만 함수를 재생성한다 ---> 효율적 (남발하면 메모리에 무리가 감)
renderList
const renderList = useCallback(()=>{
let newArr = list.map((v,k)=>{
return <li key = {k}>{v}</li>
})
return newArr
}, [list])
useCallback 은 몰라도 상관없지만 최적화를 하기위한 도구이다 ! 필요한 공간이 있을 떄가 있을 때 !
100% 필요한 것은 아니라고 함,,,,
과제 : 위의 전체 완성 코드 안보고 쓰기
'블록체인 기반 핀테크 및 응용 SW개발자 양성과정 일기' 카테고리의 다른 글
[86일차]20210713 리액트 useReducer Context styled-component Ref Css TicTacToe 만들기 (0) | 2021.07.13 |
---|---|
[85일차 복습] React Hooks useMemo useCallback (0) | 2021.07.13 |
[84일차]20210708 (0) | 2021.07.12 |
[83일차]20210708 React 기본 개념 복습 (0) | 2021.07.08 |
[81일차 복습] css 반응형 햄버거 (삼선) 메뉴 만들기 / 리액트 라우터 react-router-dom (1) | 2021.07.07 |