1. React Class 형으로 버튼을 눌러서 up down 되도록 만들기
<!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>
<h2>up and down btn</h2>
<div id="root"></div>
<script type="text/babel">
class CounterClass extends React.Component {
state = {
number : 0
}
up =()=>{
this.setState({
number: this.state.number +1
})
}
down =()=>{
this.setState({
number : this.state.number -1
})
}
render() {
return (
<>
<h3>{this.state.number}</h3>
<button onClick={this.up}>-1</button>
<button onClick={this.down}>+1</button>
</>
)
}
}
ReactDOM.render(
<CounterClass />,
document.querySelector('#root')
)
</script>
</body>
</html>
클래스형은 바뀐부분이 생기면 render() 부분만 재 랜더가 된다.
2. 위의 내용을 함수형 Function으로 다시 만들어 보기
<!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>
<h2>up and down btn</h2>
<div id="root"></div>
<script type="text/babel">
const CounterFn = ()=>{
const [number, setNumber] = React.useState(0)
const down = () => {
setNumber(number-1)
}
const up = () => {
setNumber(number+1)
}
return(
<>
<h3>{number}</h3>
<button onClick={down}>-1</button>
<button onClick={up}>+1</button>
</>
)
}
ReactDOM.render(
<CounterFn />,
document.querySelector('#root')
)
</script>
</body>
</html>
함수형의 경우 setState의 일부분이 바뀌면 전체가 모두 재랜더링이 된다. (연관없는 함수들, 코드까지) -> 함수형으로 많은 연산을 포함하게되면 안좋음 -> 이제 요거를 커버할 수 있는 Hooks 를 배울 것 !
Memoization
메모이제이션이란 ? 컴퓨터가 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산을 할 필요가 없어져 실행 속도가 빨라지는 기술 -> 동적 계획법(DP)의 핵심 기술
하지만 메모리를 사용하기 때문에 속도를 받고 '성능'을 준 셈 (자원의 교환) -> 위의 기술을 너무 남발하게 되면 성능이 저하된다. 꼭 필요한 부분에 사용하기 !
메모이제이션을 실행해보기 좋은 예, 피보나치 수열이 있다.
피보나치 수열이란 ?
첫 째항, 둘 째항이 무조건 1 이며 그 뒤로 이전의 n-1, n-2 의 위치의 값을 더해서 n 값을 만드는 수열
피보나치 수열을 JS 알고리즘으로 표현하면
<!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>Document</title>
</head>
<body>
<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>
</body>
</html>
그런데 숫자가 n = 50만 넣어도 작동이 아~주 오래 걸린다. 재귀함수로 계속 돌고 있기 때문에 효율이 안좋다.
예를 들어,
n = 3일 때 fibo() 함수는 2번이 실행되고 n = 4일 때는 fibo() 함수가 4번이 실행된다.
n = 5일 때는 fibo() 함수가 8번, n = 6일 떄는 14번이나 재귀함수가 실행된다.
만약 이전에 계산한 것을 메모리에 저장하고 필요할 때 메모리에 저장된 값을 꺼내서 사용하면 ? -> 메모이제이션 !
위의 비효율성을 줄일 수 있다. 즉, 한 번 연산했던 이전의 값을 저장해두었다가 재사용하는 기술
memo 라는 곳에 연산이된 값을 넣고 n 연산을 할 때 해당 n이 memo 안에 있다면 연산하지않고 해당 값을 꺼내오기만 하면 된다.
<!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>Document</title>
</head>
<body>
<script type="text/javaScript">
let 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[n]= result;
}
return result;
}
</script>
</body>
</html>
Memoization 만들어보기
1. 웹팩 수동 설치된 기본 코드 준비 (index.jsx, App.jsx, webpack.config.js, package.json)
App Component는 함수형으로 !
https://blckchainetc.tistory.com/255
2. memo 폴더, memo.jsx 파일 생성, 코드작성, App과 연결
이제
<- 요 기본틀을 자동으로 구현되게 만들기
3. 내용쓰고 추가 누르면 리스트 추가되도록 만들기
1) input box onChange, value 변동시키기
2) submit 제출하면 input box reset
3) <li>로 배열로 담으면 jsx가 알아서 리스트로 뿌려줌 -> 배열에 담아 map으로 뿌리기
전체 코드
import React, {useState} from 'react';
const Memo =() =>{
const [username, setUsername] = useState('');
const [list,setList] = useState([]);
const changeValue = (e) =>{
setUsername(e.target.value)
}
const submit =(e)=>{
e.preventDefault()
let new_list = [...list]
console.log(new_list)
new_list.push(username)
setList(new_list)
setUsername('')
}
const renderList =()=>{
return(
list.map((v,i)=>{
return <li key={i}>{v}</li>
})
)
}
return(
<>
<h2>회원리스트({list.length})</h2>
<form onSubmit={submit}>
<input type="text" name="username" value={username} onChange={changeValue}/>
<button type="submit">
추가
</button>
</form>
<ol>
{renderList()}
</ol>
</>
)
}
export default Memo
list의 요소를 하나하나 <li>에 담아 배열로 만들어 return 하는 3가지 방법
1)
let V = list.map((v,i)=>{
return <li key={i}>{v}</li>
})
return V
2)
return(
list.map((v,i)=>{
return <li key={i}>{v}</li>
})
)
3)
let newArr = [];
for (let i=0; i<list.length; i++){
newArr.push(<li key={i}>{list[i]}</li>)
}
return newArr
useMemo
위에서 함수형, 클래스형 차이를 설명한 것처럼 함수형의 경우 한 부분인 setState가 되면 모든 전체 코드가 rerender 되어 비효율적이다. useMemo 를 사용해서 메모리에 메모하듯이 값을 저장해놓고 값의 변경이 일어날 경우만 render되도록 만드는 것이 효율적 ! 위의 코드에서 onChange 함수의 경우, inputbox에 글자를 쓸 때마다 rerendering 되고 있어서 이 부분을 useMemo로 수정해보기
회원리스트 ({list.length})부분 useMemo로 수정하기
1. useMemo 'react' 로부터 가져오기 (react.useMemo() 처럼 매서드가 react안에 존재 -useState처럼)
import React, {useState, useMemo} from 'react';
2. useMemo 사용하기
const userCount = useMemo(()=>{
return list.length
}, [list.length])
return(
<>
<h2>회원리스트({userCount})</h2>
<form onSubmit={submit}>
useMemo의 첫 번째 인자 : 함수
두 번째 인자 : 변경 감지할 값
두 번째 인자에 넣은 list.length 값이 변경이 되면 해당 값을 메모리에 저장하고 ---> render
사실 list의 length값 변경은 '추가' 버튼을 눌러야 변경(render)되는 것 같지만 input change 할 때마다 모든 함수들이 재선언되고 있다.
함수에서 useMemo, 클래스의 경우 비슷한 기능으로 pure Component가 있다고 한다.
useCallback
useCallback과 useMemo의 차이 :
useCallback은 함수, Function을 memoization, 재사용할 때 사용
useMemo는 변수의 값 (특정 결과값)을 memoizationg, 재사용할 때 사용
1) useCallback 가져오기
import React, {useState, useMemo, useCallback} from 'react';
2) useCallback 을 onChange 함수에 사용해보기
const input_userName=useCallback((e)=>{
setUserName(e.target.value)
},[])
.
.
.
<h2>회원리스트({userCount})</h2>
<form onSubmit={submit}>
<input type="text" name="userName" value={userName} onChange={input_userName}/>
useCallback도 첫 번째 인자값: 함수 , 두 번째 인자값 : 변화 감지 기본값
[] 빈 배열은 가장 처음 Component를 실행하고 render될 때 input_userName 함수를 생성하겠다. 라는 의미라고 함
근데 저 빈 배열이 없어도 잘 되어서 아직 정확히 잘 모르겠다. username의 값은 string인데...
* google에서 찾아본 useCallback
useCallback을 사용하지않은 함수 컴포넌트들은 rerendering 될 때마다 새로 만들어진다. 함수를 선언하는 자체는 메모리, CPU 리소스를 많이 차지하는 작업은 아니어서 함수를 매번 새로 선언한다고 그 자체만으로 큰 부하가 생기지는 않지만 useCallback을 사용해서 저장해두고 필요할 때 재사용하는 것은 필요하다.
이유 : 최적화
3) submit 함수를 useCallback 해보기
// const submit = (e)=>{
// e.preventDefault()
// let new_list = [...list]
// new_list.push(userName)
// setList(new_list)
// setUserName('')
// }
const submit = useCallback((e)=>{
e.preventDefault()
let new_list = [...list]
new_list.push(userName)
setList(new_list)
setUserName('')
},[list, userName])
return(
<>
<h2>회원리스트({userCount})</h2>
<form onSubmit={submit}>
안의 내용은 똑같고 초기 값(두 번째 인자값) 에 [list, userName]을 넣어준다. submit 함수의 내용을 보면 setList, setUserName으로 해당 값들을 변경하고 있어서 두 개 모두 넣기 !
4) listRender함수도 변경해보기
// const listRender=()=>{
// return(
// list.map((v,i)=>{
// return <li key={i}>{v}</li>
// })
// )
// }
const listRender = useCallback(()=>{
return(
list.map((v,i)=>{
return <li key={i}>{v}</li>
})
)
},[list])
근데 두 번째 인자값 [list]를 빼도 실행은 되는데 이 점이 궁금하다.
* useCallback의 두 번째 인자
두번째 인자의 배열** 은 의존성을 의미 - useCallback 함수 내에서 의존하는 상태값이 있다면 반드시 두 번째 인자 배열에 명시해야 한다.
요 부분 아래를 보면 두 번째 인자값 이해가 잘 간다. 위의 전체 코드에 버튼들과 count 변수 추가해서 실험해보기
useCallback 의 두 번째인자값을 안넣었을 때
const Memo = () =>{
const [userName, setUserName] = useState('')
const [list, setList] = useState([])
const [count, setCount] = useState(0)
.
.
.
const up = useCallback(()=>{
setCount(count+1)
},[count])
const consoleLog = useCallback(()=>{
console.log(count)
},[])
return(
<>
<p>{count}</p>
<button onClick = {up}>+1</button>
<button onClick= {consoleLog}>console.log찍기</button>
실행 순서
1) console.log찍기 버튼 클릭
2) +1 버튼 클릭
3) 다시 console.log찍기 버튼 클릭
1) 번 0 출력
2) 번 클릭 -> count +1 = 1
3) 번 클릭 -> 0 출력
두 번째 인자값이 없어서 참조할 곳이 없다. 함수 안의 {count}는 최초 설정(첫 render)했을 떄 기본값이었던 0 을 계속 return 한다.
두 번째 인자값을 넣었을 때
const [count, setCount] = useState(0)
const consoleLog = useCallback(()=>{
console.log(count)
},[count])
<p>{count}</p>
<button onClick = {up}>+1</button>
<button onClick= {consoleLog}>console.log찍기</button>
함수 실행할 때 참조할 count 값이 배정되어 있어서 해당 component 안의 count를 가져와 console.log를 찍을 수 있다.
끄읏
'블록체인 기반 핀테크 및 응용 SW개발자 양성과정 일기' 카테고리의 다른 글
[86일차 복습] React useReducer Context syled-component로 TicTacToe만들기 (0) | 2021.07.14 |
---|---|
[86일차]20210713 리액트 useReducer Context styled-component Ref Css TicTacToe 만들기 (0) | 2021.07.13 |
[85일차]20210712 React hooks API useMemo useCallback (0) | 2021.07.12 |
[84일차]20210708 (0) | 2021.07.12 |
[83일차]20210708 React 기본 개념 복습 (0) | 2021.07.08 |