일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 시퀄라이즈공부
- sementicversion
- node
- 시퀄라이즈
- 리액트기초
- React Component
- nodeJS
- component
- mongoose
- NPM
- express-generator
- 리액트컴포넌트
- npm명령어
- NoSQL
- 제로초예제
- MongoDB
- 리액트
- mongo
- React
- 리액트스타디
- nodejs교과서
- 클래스컴포넌트
- sequelize
- Today
- Total
개발노트
12. hooks / useEffect , useMemo 본문
useState, useRef 로 변경
import React, { useState , useRef , useEffect} from 'react';
import Ball from './Ball';
function getPickNumbers(){
console.log('pickNum START');
const numArray = Array(45).fill().map( (v,i) => i + 1 );
const shuffle = [];
while( numArray.length>0 ){
shuffle.push( numArray.splice( Math.floor( Math.random() * numArray.length ) ,1)[0] );
}
const bonusNumber = shuffle[shuffle.length -1];
const rltNumbers = shuffle.slice(0,6).sort( (p,c)=>p-c );
return[...rltNumbers, bonusNumber];
};
const Lotto = () =>{
const [pickNumbers, setPickNumbers] = useState(getPickNumbers());
const [balls, setBalls] = useState([]);
const [bonus, setBonus] = useState(null);
const [rePick, setRePick] = useState(false);
const timer = useRef([]);
// const lottoStarting = ()=>{
// for( let i=0; i<pickNumbers.length-1; i++){
// timer.current[i] = setTimeout( ()=>{
// setBalls( (prevBalls)=>{ return [...prevBalls, pickNumbers[i]] } )
// } , (i+1)*100 ) ;
// }
// setTimeout(
// ()=>{
// setBonus(pickNumbers[6]);
// setRePick(true);
// }
// ,700
// )
// }
const onClickRePick = () =>{
//초기화
setPickNumbers(getPickNumbers());
setBalls([]);
setBonus(null);
setRePick(false);
timer.current=[];
}
// componentDidMount(){
// this.lottoStarting ();
// }
// componentWillUnmount(){
// this.timer.forEach( (v)=>{ clearTimeout(v) });
// }
// componentDidUpdate( prevProps, prevState){
// if( this.state.balls.length === 0 ){
// this.lottoStarting ();
// }
// }
return(
<>
<h1>당첨숫자</h1>
<ul id="rlt_box">
{ balls.map( ( v )=>( <Ball key={v} num={v} /> ) )}
{ bonus && <Ball num={bonus} /> }
</ul>
{ rePick && <button onClick={ rePick ? onClickRePick :()=>{} } >한번더</button> }
</>
)
}
export default Lotto;
componentDidMount, componentwillUnmout, componentDidUpdate를
useEffect 로 바꾸기
▶use Effect기본 설명
useEffect(()=>{} , [])
이렇게 의존성배열이 [] 비어있으면 componentDidMount와 동일한 기능
useEffect(()=>{} , [ something ])
배열에 요소가 있으면 componentDidMount 와 componentDidUpdate 둘다 수행 (업데이트아님)
useEffect(()=>{
return()=>{ //unmount }
} , [ something ])
리턴부분이 componentwillUnmout
▶useEffect를 componentDidUpdate만 사용하고 싶은 경우
const flag =useRef( false );
useEffect (()=>{
if( !flag.current ){
flag.current = true;
}else{
// update..event
}
},[ /*바뀌는값*/ ])
이런식으로 flag를 선언해서. (화면에보여줄게아니니 state가 아닌 useRef)
제일처음didMount때는 flag를 true 로만 바꾸고
update때만 실행되게 만들어서 사용할수 있다.
▶ex) 기존 클래스컴포넌트의 세가지 이벤트를 -> useEffect로 변경
useEffect( ()=>{
console.log(useEffect);
//componentDidMount
for( let i=0; i<pickNumbers.length-1; i++){
timer.current[i] = setTimeout( ()=>{
setBalls( (prevBalls)=> [...prevBalls, pickNumbers[i]] )
} , (i+1)*100 ) ;
}
timer.current[6] = setTimeout(
()=>{
setBonus(pickNumbers[6]);
setRePick(true);
}
,700
)
return()=>{
timer.current.forEach( (v)=>{ clearTimeout(v) }); //componentWillUnmount
}
},[balls.length === 0]) //componentDidUpdate조건
// componentDidMount(){
// this.lottoStarting ();
// }
// componentWillUnmount(){
// this.timer.forEach( (v)=>{ clearTimeout(v) });
// }
// componentDidUpdate( prevProps, prevState){
// if( this.state.balls.length === 0 ){
// this.lottoStarting ();
// }
// }
이렇게 할경우.. balls의 초기값이 [] 여서,
const [balls, setBalls] = useState([]);
버튼눌러서초기화시킬때 발생해야되는데 로딩될때 그냥 두번실행되는 문제발생.
조건을 바꾼다.
의존성배열에 timer.current를 넣어줌.
useEffect( ()=>{
//componentDidMount
for( let i=0; i<pickNumbers.length-1; i++){
timer.current[i] = setTimeout( ()=>{
setBalls( (prevBalls)=> [...prevBalls, pickNumbers[i]] )
} , (i+1)*100 ) ;
}
timer.current[6] = setTimeout(
()=>{
setBonus(pickNumbers[6]);
setRePick(true);
}
,700
)
return()=>{
timer.current.forEach( (v)=>{ clearTimeout(v) }); //componentWillUnmount
}
},[timer.current]) //componentDidUpdate조건 ////////
초기값이
const timer = useRef([]);
이고
버튼클릭시
timer.current=[];
인 타이머로 !
timer.current=[]; 이때 이전 current와 달라지는것.
timer.current[6] = someting;
※current배열에 요소로 넣어준 거라서 바뀌는게아님
그래서 바뀌는걸 =[] (빈배열대입시킬때) 감지함
성능최적화
훅스 자체 특성이 상태변할때 (훅스)함수 전체가 재실행 되서,
이렇게 getPickNumbers 함수가 반복되서 호출되는 문제를 확인할 수 있는데,
이런 함수는 로또숫자들을 기억해두기위해서 useMemo를 사용한다.
함수실행한 결과값을 저장해 두려면
useMemo
const lottoNumbers = useMemo( ()=>{ return //계속실행됬던문제의함수. },[]);
이런식으로 만들어서 그 함수를 담아줌.
[] 의 인자가 바뀌지 않는한 useMemo에 담아둔 함수는 다시실행되지 않음
[] 의 인자가 바뀌면 다시실행됨.
************************************************
**그래서 만약 이렇게 다시버튼 눌렀을때
번호7개가져오는함수를 실행하지않고
useMemo에 저장된( 번호7개가져오는함수 결과값) 값을 붙여넣을경우
계속 같은 숫자가 리턴 됨
************************************************
함수자체를 기억
useCallback
useCallback( ()=>{} , [] )
onClickRestart를useCallback으로 감싸면 onClickRestart이 함수자체를 기억
함수컴포넌트가 재실행 되도 그 함수를 재생성하지 않음.
여기도 마찬가지로 []의존성배열에 상태값바뀌면 다시발생시킬 상태값이름 넣어줘야
그 값이 업데이트 된다.
const onClickRePick = useCallback( () =>{
console.log( pickNumbers);
setPickNumbers( getPickNumbers() ); //
}
위의 경우에서 getPickNumbers 로 계속해서 새로웃 숫자 7개를 받아와서
console.log 안에 pickNumbers 가 계속해서 받아온 새로운 숫자일 것으로 예상하지만,
(pickNumbers라는 애의 상태값을 의존성배열에 추가하지 않았으므로)
첫번째 배열을 기억해서 그것만 리턴한다
의존성배열에 추가하면 변화된 상태 반영 됨 ~~
useCallback을 필수로 적용해야될 경우
자식컴포넌트에 props로 함수를 넘길때는 useCallback을 써야,
자식컴포넌트에서 부모가 계속 함수 바꿀때마다, 매번 새로운props을 준다고
매번 리렌더링을 일으키지 않는다
//Lotto.jsx
import React, { useState , useRef , useEffect, useMemo , useCallback} from 'react';
import Ball from './Ball';
function getPickNumbers(){
console.log('pickNum START');
const numArray = Array(45).fill().map( (v,i) => i + 1 );
const shuffle = [];
while( numArray.length>0 ){
shuffle.push( numArray.splice( Math.floor( Math.random() * numArray.length ) ,1)[0] );
}
const bonusNumber = shuffle[shuffle.length -1];
const rltNumbers = shuffle.slice(0,6).sort( (p,c)=>p-c );
return[...rltNumbers, bonusNumber];
};
const Lotto = () =>{
const lottoNumbers = useMemo( ()=>{ return getPickNumbers() },[]);
const [pickNumbers, setPickNumbers] = useState( lottoNumbers );
const [balls, setBalls] = useState([]);
const [bonus, setBonus] = useState(null);
const [rePick, setRePick] = useState(false);
const timer = useRef([]);
const onClickRePick = useCallback( () =>{
//초기화
console.log( pickNumbers);
setPickNumbers( getPickNumbers() ); //
setBalls([]);
setBonus(null);
setRePick(false);
timer.current=[];
} ,[pickNumbers])
useEffect( ()=>{
console.log("useEffect");
for( let i=0; i<pickNumbers.length-1; i++){
timer.current[i] = setTimeout( ()=>{
setBalls( (prevBalls)=> [...prevBalls, pickNumbers[i]] )
} , (i+1)*100 ) ;
}
timer.current[6] = setTimeout(
()=>{ setBonus(pickNumbers[6]);
setRePick(true); } ,700 )
return()=>{
timer.current.forEach( (v)=>{ clearTimeout(v) });
}
},[timer.current])
return(
<>
<h1>당첨숫자</h1>
<ul id="rlt_box">
{ balls.map( ( v )=>( <Ball key={v} num={v} /> ) )}
{ bonus && <Ball num={bonus} /> }
</ul>
{ rePick && <button onClick={ rePick ? onClickRePick :()=>{} } >한번더</button> }
</>
)
}
export default Lotto;
기타
-Hooks는 순서가 중요하기때문에 조건문안에 넣지 않는다
-useEffect안에 useEffect넣으면 안됨.
'React > basic' 카테고리의 다른 글
13. (2) useReducer (0) | 2020.07.29 |
---|---|
13. useReducer (0) | 2020.07.28 |
12. componentDidUpdate , setInterval (0) | 2020.07.27 |
11 (2)hooks / useEffect (0) | 2020.07.26 |
11. react life cycle (0) | 2020.07.26 |