리엑트 hooks
useState
컴포넌트 내부에서 사용되는 데이터를 설정하고 설정한 데이터 값을 바꾸는 기능을 한다
ex)
import React, { useState } from 'react';
const Test2 = () => {
const [count,setCount] = useState(0)
const up = () =>{
setCount(count+1)
}
const down = () =>{
setCount(count-1)
}
return (
<div>
<div>{count}</div>
<button onClick={up}>+1</button>
<button onClick={down}>-1</button>
</div>
);
};
export default Test2;
위코드를 해석해보면 count와 setCount를 구조분해할당 하여 useState를 할당하였고
count는 useState의 역할을 setCount는 setState의 역할을 하게된다
이때 useState()안에는 setCount로 값을 변경하기 전 기본값을 명시하는 기능을 한다
useEffect
useEffect는 componetDidMount와 componetDidUpdate가 합쳐진 기능이다 컴포넌트가 렌더링 될 때마다 실행할 작업 을 설정 할 수 있다
즉 컴포넌트가 처음에 화면과 연결되는 경우(componetDidMount), 컴포넌트가 업데이트되는경우(componetDidUpdate) 에 어떠한 동작을 실행 할 수 있는 기능을 가진 hook이다
useEffect에
1번 파라미터는 useEffect가 동작할 내용을 콜백함수로명시한다
1번 파라미터에 콜백함수는 컴포넌트가 렌더링 될때
즉 컴포넌트가 처음 브라우저와 연결되어 렌더링 될 때(componetDidMount)
컴포넌트가 업데이트 되어 리렌더링 될 때(componetDidUpdate) 실행된다
2번 파라미터는 배열의 형태이고 이 배열은 DependencyArray즉 의존성 배열이다
2번 파라미터의 의존성 배열은
컴포넌트가 처음 브라우저와 연결되어 렌더링 될 때(componetDidMount)
의존성 배열안에 명시한 내용의 값이 변경될때 실행된다
만약 2번파라미터에 빈배열을 넣는다면 의존성 배열에 명시된 내용이 없기 때문에 컴포넌트가 처음 렌더링될때만 동작한다
만약 2번 파라미터에 의존성 배열을 아예 명시하지 않는다면 컴포넌트가 랜더링 될때마다 1번파라미터의 명시된 내용을 호출한다 이것은 즉 state가 바뀔때마다 함수를 호출하게 될 수 있는 상황으로 이어 질 수 있다고 받아 들일 수 있고 자칫 무한 루프에 빠질 수 있는 가능성이 존재한다 때문에 state가 업데이트 될때 실행할 내용이 없더라도 의존성 배열을 빈배열이라도 명시하는것이 좋다고 생각한다
ex)
const Test2 = () => {
const [name,setName] = useState('')
const [nickName,setNickName] = useState('')
useEffect(()=>{
console.log('렌더링이 완료되었습니다')
console.log({
name,
nickName
})
})
const changeName = e =>{
setName(e.target.value)
}
const changeNickName = e =>{
setNickName(e.target.value)
}
const reset = () => {
setName('')
setNickName('')
}
return (
<>
<label>이름 생성<input onChange={changeName} value ={name}/></label>
<label>닉네임 생성<input onChange={changeNickName} value={nickName}/></label>
<div>이름:{name}</div>
<div>닉네임:{nickName}</div>
<button onClick={reset}>초기화</button>
</>
);
};
useEffect는 처음 랜더링 될 때(componetDidMount)와,업데이트 될 때를 분리하여 사용 할 수도 있다
useEffect가 처음 랜더링 될 때에만 로직을 실행 시키고 싶다면 useEffect에 2번 파라미터에 빈 배열을 명시하면 된다
ex)useEffect를 처음 렌더링 될 때만 실행 시키는 경우
useEffect(()=>{
console.log('처음 렌더링 될때만 실행됩니다')
},[])
useEffect에 1번 파라미터는 컴포넌트가 처음렌더링 될 때 실행 할 로직을 명시하고
2번 파라미터에는 컴포넌트가 업데이트 될 때 변경사항을 감지할 내용을 배열의 형태로 명시한다
위 코드에서 2번 파라미터에 []은 빈배열이고 즉 컴포넌트가 업데이트 될 때 변경사항을 감지 하는 내용이 없기 때문에
처음 랜더링 될 때(componetDidMount)에만 useEffect를 사용 할 수 있게 되는 것이다
특정값이 업데이트 되는경우에 useEffect사용
useEffect(()=>{
console.log(name,nickName)
},[name])
위 코드와같이 특정 name과 nickName을 콘솔에 찍고 있지만 2번 파라미터에 배열안에
name을 명시하고있다 때문에 name의 값과, nickName의 값을 변경해도 useEffect는 name의 변경 사항만을 감지 하여
1번 파라미터를 실행시킨다 즉 name과nickName 둘다 변경해도 console창에는 name에 변경 사항만 반영 된다.
ex)react로 만든 모달 창 예시
//부모컴포넌트
const App = () => {
const [visible, setVisible] = useState(false);
const click = () => {
setVisible(!visible);
};
return (
<>
<button onClick={click}>
{visible ? '숨기기' : '보이기'}
</button>
<div>{visible && <Test2 />}</div>
</>
);
};
//자식 컴포넌트
const Test2 = () => {
const [name,setName] = useState('test1')
const [nickName,setNickName] = useState('test2')
useEffect(()=>{
console.log('effect')
console.log(name)
return () =>{
console.log('cleanup')
console.log(name)
}
},[name])
const changeName = e =>{
setName(e.target.value)
}
const changeNickName = e =>{
setNickName(e.target.value)
}
const reset = () => {
setName('')
setNickName('')
}
return (
<>
<label>이름 생성<input onChange={changeName} value ={name}/></label>
<label>닉네임 생성<input onChange={changeNickName} value={nickName}/></label>
<div>이름:{name}</div>
<div>닉네임:{nickName}</div>
<button onClick={reset}>초기화</button>
</>
);
};
ex) useEffect를 이용한 타이머 예시
//부모컴포넌트
const App = () => {
const [showTimer, setShowTimer] = useState(false);
const isShow = () => {
setShowTimer(!showTimer);
};
return (
<>
{showTimer && <Test2 />}
<button onClick={isShow}>Toggle Timer</button>
</>
);
};
//자식 컴포넌트
const Test2 = () => {
useEffect(()=>{
const timer = setInterval(()=>{
console.log('타이머 돌아가는듕...')
},1000)
return () =>{
clearInterval(timer)
}
},[])
return (
<>
<div>타이머를 시작합니다. 콘솔을 보세요!</div>
</>
);
};
위처럼 useEffect에 1번 파라미터에서 return 키워드를 사용하면 컴포넌트가 화면에서 사라질때
즉 (unmount) 되었을때 실행할 내용을 명시한다
useRef
useRef는 2가지 역할을 하는 Hook이다
1 DOM에 직접적으로 접근하는 역할을 한다
2 특정값을 변경했을때 컴포넌트를 리렌더링 하지 않을때 사용한다
변수는 값을 변경했을때 컴포넌트가 리렌더링 되면 값이 초기화 되지만
useRef는 변수와 달리 변경값을 저장하고있기 때문에 초기화 되지않고 변경값을 가지고 있다
때문에 useRef는 특정 값에 변경사항을 유지하며, 리렌더링을 원하지않을때 사용된다
즉 ref는 반응성을 가지고 있지않기 때문에 화면 갱신이 이루어지지 않는 다는점을 주의하자
ex)ref와 변수를의 차이를 통해 ref기능 이해하기
const Test2 = () => {
const [render,setRender] = useState(0)
const handleRender = () =>{
setRender(render+1)
}
const inputRef = useRef(0)
const handleInputRef = () =>{
inputRef.current = inputRef.current+1
console.log(inputRef.current)
}
let variable = 0
const handleVariable = () =>{
variable+=1
console.log(variable)
}
return (
<>
<div>Ref 값:{inputRef.current}| 변수 값 : {variable}</div>
<button onClick={handleInputRef}>클릭후 console에서 ref값을 확인하세요!</button>
<button onClick={handleVariable}>클릭후 console에서 변수값을 확인하세요!</button>
<button onClick={handleRender}>리렌더링 버튼</button>
<div><b>변수와 ref는 리렌더링 되지않기 때문에 화면에서 변경 사항을 확인하고 싶다면 리렌더링 버튼을 클릭하여 리렌더링 시켜주세요!</b></div>
</>
);
};
위코드에서 inputRef라는 useRef가 사용된 변수를 사용할 때 current를 메소드 체이닝 해서 표현하는 이유는
useRef라는 기능이 사용된 inputRef를 콘솔창에 출력해보면 current라는 객체가 출력되는데
해당 객체 내부를 확인해보면 현제 ref가 가지고 있는 값이 저장되어 있다 즉 current는 현제 ref의 값을 의미한다
때문에 ref의 실제 값을 사용할 때에는 메소드체이닝을 이용해 current를 붙여줘야한다
ex)ref를 이용해 DOM에 직접접근하기
const Test2 = () => {
const [value,setValue] = useState('')
const inputRef = useRef(null)
const changeInput = e =>{
setValue(e.target.value)
}
const enter = e =>{
if(e.key === 'Enter'){
res()
}
}
const res = () =>{
alert(value)
setValue('')
inputRef.current.focus()
console.log(inputRef)
}
return (
<>
<input
ref={inputRef}
onChange={changeInput}
onKeyDown={enter}
value={value}
placeholder='아무 내용이나 입력 해 보세요!'
/>
<button onClick={res}>확인</button>
</>
);
};
위코드처럼 특정변수에 useRef를 할당한 후 html영역에 속성 ref와 useRef가 할당된 변수를 연결하면
useRef를 사용한 변수에 해당 요소를 할당 할 수 있다 실제로 current안에 요소가 저장되어 있는 상태이기 때문에
사용할때는 메소드 체이닝으로 current를 붙여줘야한다
useMemo
컴포넌트를 최적화 할때 사용되는 훅이다
useMemo에서 Memo는 Memoization이다
Memoization이란?
이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술이다
즉 컴포넌트가 렌더링 될 때마다 따로 함수를 호출하지 않아도 메모리에 저장된 함수를 계산한 내용을 꺼내와서 사용한다는 의미이다
뷰에서 computed와 같은 역할을 하는 hooks이다
useMemo의
1번 파라미터는 콜백함수로써 어떤 내용을 실행할 것인지 명시하고, 실행결과를 메모리에 저장한다
2번 파라미터는 의존성배열을 명시하는데 의존성 배열안에 명시된 내용이 업데이트 될 때마다 1번 파라미터의 실행결과를 호출한다
주의사항!
useMemo는 메모리에 값을 저장하기 때문에 메모리 자원을 많이 요구한다 때문에 꼭 필요 할때만 사용해야 성능최적화가 가능하다
ex)
const getAverage = numbers =>{
console.log('평균값 계산듕...')
if(numbers.length===0) return 0
const sum = numbers.reduce((a,c)=>a+c)
return sum/numbers.length
}
const Test2 = () => {
const [list,setList] = useState([])
const [number,setNumber] = useState('')
const changeHandler = e =>{
setNumber(e.target.value)
}
const insertHandler = () =>{
const nextList = list.concat(parseInt(number))
setList(nextList)
setNumber('')
}
const average = useMemo(()=>{
return getAverage(list)
}, [list])
return (
<>
<input
value={number}
onChange={changeHandler}
onKeyDown={insertHandler} />
<button onClick={insertHandler}>등록</button>
<ul>
{list.map((item,index)=>(
<li key={index}>{item}</li>
))}
</ul>
<div>
<b>평균값:</b> {average}
</div>
</>
);
};
useCallback
컴포넌트를 최적화 할때 사용되는 훅이다
컴포넌트가 리렌더링 될때마다 컴포넌트안에 명시된 함수가 새로 만들어 지게 되는데 useCallback을 사용하면
함수가 메모리에 저장되어 있는 상태가 되어 함수가 새로 만들어 지는 상황을 방지 할 수 있다
useCallback은
1번 파라미터에 메모리에 저장할 함수 로직을 명시한다
2번 파라미터에는 배열([])의 형태로 명시하는데 해당 배열은 의존성 배열로써 배열 안에 명시한 변수가 변경될때마다(업데이트) 1번파라미터에 콜백함수를 재실행하여 메모리에 저장된 값을 갱신한다
ex)
const Test2 = () => {
const [number,setNumber] = useState(0)
const someFunction = useCallback(()=>{
console.log(`someFunc:number: ${number}`)
return
},[number])
const numberChange = e =>{
setNumber(e.target.value)
}
useEffect(()=>{
console.log('someFunction이 변경되었습니다')
},[someFunction])
const enter = e =>{
if(e.key === 'Enter')
someFunction()
}
return (
<>
<input
type="number"
value={number}
onChange={numberChange}
onKeyDown={enter}
/>
<button onClick={someFunction}>클릭 후 콘솔창을 확인해 주세요!</button>
</>
);
};
useReducer
useReducer는 다양한 상태(state)를 변경할때 사용하는 Hook이다
useReducer를 사용할땐 반드시 불변성을 지켜야한다
useReducer를 사용하기위해선 3가지를 알아야한다
1 Reducer : state를 업데이트 해주는 역할을 한다
2 Dispatch: state를 업데이트 해 달라고 Reducer에게 요청 하는 역할을 한다
3 Action : Dispatch로 state를 업데이트 해 달라고 Reducer에게 요청할때 어떻게 state 를업데이트 해야하는지에 대한 내용을 명시하는 역할을 한다
useReducer안에 1번 파라미터는 reducer함수를 넣고 2번파라미터에는 reducer함수에 기본값을 설정한다
1번파라미터에 들어가는 reducer 함수는 따로 작성 해 줘야한다
reducer 함수에는 2개의 파라미터가 사용되는데
1번파라미터에는 현제 state를 받고 2번 파라미터에는 action을 받는다
ex)
const ACTION_TYPE = {
deposit : '예금',
withdraw : '출금'
}
const reducer = (state,action) =>{
//dispatch가 호출되면 실행할 로직을 명시하는 영역이다
console.log('dispatch를 호출',state,action)
// 한가지 case만 출력하지 않기 때문에 case에 따라 다른 결과를 반환해야하므로 주로 if문이나 case문을 사용하여
//action에 타입에 따라 다른 결과를 반환시킨다
switch (action.type){
case ACTION_TYPE.deposit:
return state + action.payload
case ACTION_TYPE.withdraw:
return state - action.payload
default:
return state
}
}
const Test2 = () => {
const [number,setNumber] = useState(0)
const [money,dispatch] = useReducer(reducer,0)
const changeNumber = e =>{
setNumber(parseInt(e.target.value))
}
const inputMoney = () =>{
dispatch({
//reducer 함수에 전달할 action을 명시하는 영역이다
type: ACTION_TYPE.deposit,
payload:number
})
}
const outputMoney = () =>{
dispatch({
//reducer 함수에 전달할 action을 명시하는 영역이다
type:ACTION_TYPE.withdraw,
payload:number
})
}
return (
<>
<h2>useReducer 은행에 오신것을 환영합니다</h2>
<p>잔고:{money}원</p>
<input
type="number"
value={number}
onChange={changeNumber}
step='1000'
/>
<button onClick={inputMoney}>예금</button>
<button onClick={outputMoney}>출금</button>
</>
);
};
ex)
const reducer = (state,action) =>{
switch(action.type){
case'INCREMENT':
return {value:state.value+1}
case 'DECREMENT':
return {value:state.value-1}
default:
return state
}
}
const Test2 = () => {
const [state,dispatch] = useReducer(reducer,{value:0})
return (
<>
<p>현재 카운터 값은 <b>{state.value}</b>입니다.</p>
<button onClick={()=>dispatch({type:'INCREMENT'})}>+1</button>
<button onClick={()=>dispatch({type:'DECREMENT'})}>-1</button>
</>
);
};
ex2)인풋 상태 관리하기
const reducer = (state,action) =>{
return{
...state,
[action.name] : action.value
}
}
const Test2 = () => {
const [state,dispatch] = useReducer(reducer,{
name:'',
ninkName:''
})
const {name,nickname} = state
const onChange = e =>{
dispatch(e.target)
}
return (
<>
<div>
<input name = 'name' value ={name} onChange={onChange}/>
<input name = 'nickname' value={nickname} onChange={onChange}/>
</div>
<div>
<div>
<b>이름:</b>{name}
</div>
<div>
<b>닉네임:</b>{nickname}
</div>
</div>
</>
);
};
'front-end 국비과정 학습일지' 카테고리의 다른 글
국비지원 52일차 vite로 vue프로젝트 진행하는법,vuex 정리 (0) | 2022.06.19 |
---|---|
국비지원 51일차 벨로퍼트:react를 다루는 기술(react에서 사용하는 scss, styled-component ,class 작성방식) (0) | 2022.06.16 |
국비지원 49일차 벨로퍼트:react를 다루는 기술: 리엑트 LifeCycle (0) | 2022.06.14 |
국비지원 48일차 벨로퍼트:react를 다루는 기술 (이벤트 핸들링, map함수를 이용한 반복문, filter함수를이용한 요소 삭제) (0) | 2022.06.09 |
국비지원 47일차 벨로퍼트:react를 다루는 기술 (컴포넌트 import export , props, useState) (0) | 2022.06.07 |