HYEWON JUNG의 개발일지
useState, useEffect, useRef 본문
useState
가장 기본적인 react hook 이며 함수형 컴포넌트 내에서 가변상태를 갖게 한다.
const [state, setState] = useState(초기값);
이런 기본 형태에서 리렌더링을 하려면 setState를 이용해서 해야하는데 전에 count를했었는데 당시에 사용한 방식은
2023.11.01 - [React] - state , state와 불변성, 전개구문을 이용한 불변성 유지
<button onClick={()=>setCount(--count)}>-1</button>
이런 업데이트 방식이었다. 하지만 useState는 함수형 업데이트도 가능한데 함수형 업데이트란 같은 예제를
<button onClick={()=>setCount((currentCount)=>{
return --currentCount
})}>-1</button>
이런식으로 setCount안에 함수를 넣을 수 있는 것이다. 하지만 이대로는 별 차이도 없고 더 길어지기만 하는데 왜 사용하는 걸까? 이유는 useCount로 업데이트를 연속할 때 차이가 있다.
예제에서 +1은 기존의 방식으로 3번 연속하고 -1은 함수형 업데이트로 3번 연속해보겠다.
<button onClick={() => {
setCount(++count)
setCount(++count)
setCount(++count)
}}>+1</button>
<button onClick={() => {
setCount((currentCount) => --currentCount)
setCount((currentCount) => --currentCount)
setCount((currentCount) => --currentCount)
}}>-1</button>
엣.. 기존방식은 되면 안되는데... 증감연산자로 하니까 되버리네.. 왜지..
다시해보자.
//기존 업데이트 +1
<button onClick={() => {
setCount(count+1)
setCount(count+1)
setCount(count+1)
}}>+1</button>
//함수형 업데이트-1
<button onClick={() => {
setCount((currentCount) => currentCount-1)
setCount((currentCount) => currentCount-1)
setCount((currentCount) => currentCount-1)
}}>-1</button>
저장하고 버튼을 눌렀을 때 +1버튼은 여전히 +1만 되고 -1버튼은 -3이 되고 있는 것을 확인 할 수 있다.
이유는 기존의 업데이트 방식은 배치형 업데이트라고 부르는데 배치형 업데이트는 가장 마지막에 요청한 업데이트를 받아드리고 함수형 업데이트는 요청한 업데이트를 전부 받아드린다.
<button onClick={() => {
setCount(count+2)
setCount(count+5)
setCount(count+4)//이것만 반영
}}>+1</button>
배치형 업데이트에 다른 값을 넣어주어도 마지막 업데이트만 반영을 하고 엥.. 그러면 증감연산자 더 이해가 안되는디..
우선 패스..
useEffect
렌더링 될 시점에 특정한 작업을 수행해야할 때 설정하는 훅
useEffect 기본 사용법
useEffect(()=>{메인 로직})
매개변수로 콜백함수가 들어가고 렌더링이 될때마다 실행된다.
useEffect 메인로직에 console을 찍어보면
useEffect(()=>{
console.log(`hello useEffect`)
})
처음 화면이 레더링될 때 console이 찍히는 것을 볼 수 있습니다. 그렇다면 리렌더링을 하는 state를 만들어보자.
예제로 state를 만들고 useEffect의 메인로직을 콘솔로 두고 input 만들어value로 setState를 해주면 어떻게 될까
인풋의 값이 바뀔 때마다 콘솔이 찍히는 것을 볼 수 있다.
이 로직이 실행되는 순서는
- input에 값을 입력한다.
- value, 즉 state가 변경된다.
- state가 변경되었기 때문에, App 컴포넌트가 리렌더링 된다.
- 리렌더링이 되었기 때문에 useEffect가 다시 실행된다.
- 1번 → 5번 과정이 계속 순환환다.
이렇게 진행이 되는데 변화가 일어날 때마다 콘솔이 찍히는 것이 잘 보이게 콘솔에 value가같이 나오게 해보면
console.log(`hello useEffect: ${value}`)
콘솔과 value가 입력할 때마다 쌓이는 것을 볼 수 있다.
<의존성 배열 dependency array >
하지만 이렇게 계속 실행이 되면 안되고 어떤 값에 변화가 있을 때만 메인로직을 실행하게 하는 법이 있는데 useEffect의 두번째 인자로 의존성 배열을 넣는 것이다.
useEffect(()=>{
console.log(`hello useEffect: ${value}`)
},[]) // 두번째 인자로 [의존성 배열]을 넣어줌
이 코드를 실행하면 어떻게 될까.
의존성 배열이 빈값이기 때문에 첫 렌더링에 콘솔만 찍히고 인풋값을 입력해도 콘솔이 찍히지 않는다.
만약 배열안에 값을 넣는다면 그것을 기준으로 변화가 있을 때 콘솔이 찍히게 되겠죠. 그렇다면 의존성배열에 value를 넣게 되면 인풋에 값을 입력할 때마다 콘솔이 찍히게 된다.
<clean up>
특정 컴포넌트가 죽을 때 useEffect를 사용
const [value, setValue] = useState('')
useEffect(()=>{
console.log(`hello useEffect: ${value}`)
return ()=>{
console.log(``)
}
},[])
---------하지만 안 배운 패키지가 있기에 다음에 배운다-----------
useRef
useRef는 두가지로 기억하면 좋다. DOM요소에 접근, 저장공간으로 쓰이는 것 이렇게 두 개
ref의 기본형태는
const firstRef = useRef("초기값")
콘솔로 찍어보면 console.log("ref", ref), ref가 {current: "초기값"}으로 객체에 싸여있는 것을 확인 할 수 있다. 그래서 초기값을 변경하려면 ref의 current에 접근해서 변경해야된다.
ref.current = "바꿀 값"
이렇게 ref의 값을 바꿀 순 있지만 렌더링을 하진 않는다. state같은 경우 변화가 생기면 바로 리렌더링 되어 화면에 표가 나지만 ref는 내부에서 보관만 하고 있는다.
state와 ref의 차이점을 알 수 있는 예제를 보면
const [count, setCount] = useState(0)
const statePlusHandler=()=>{
setCount(count +1)
}
return
<div style={style}>
state영역입니다.{count}<br/>
<button onClick={statePlusHandler}>state증가</button>
</div>
state의 경우엔 버튼을 누르는 순간 리렌더링이 되면서 화면에 숫자가 바뀌는 것을 확인할 수 있다. Ref의 경우를 보면
const countRef = useRef(0)
const refPlusHandler=()=>{
countRef.current++
console.log(countRef.current)
}
return
<div>
ref영역입니다.{countRef.current}<br/>
<button onClick={refPlusHandler}>ref증가</button>
</div>
Ref의 경우 버튼을 눌러도 아무런 변화가 생기지 않는다. 하지만 콘솔로 값을 확인해 보면 +1이 되고 있는 것을 알 수 있다.
이렇게 state는 리렌더링이 필요한 경우에 사용하면 되고 Ref는 리렌더링 없이 값만 저장하고 싶을 때 사용하면 된다.
DOM에 접근하기
위처럼 아이디랑 비밀번호를 입력받는 구조를 만들고 렌더링이 되면 아이디 인풋에 자동으로 포커스가 가있도록 할려면 Ref를 이용해서 가능한데
const idRef = useRef("")
useEffect(()=>{
idRef.current.focus()
},[])
<div>
아이디 :<input
type='text'
ref={idRef}
/>
</div>
<div>
비밀번호 :<input
type='password'
ref={passRef}
/>
</div>
input에는 ref라는 속성이 있는데 input에 ref로 idRef를 넣어줬기때문에 input에 접근이 가능하다. 그리고 렌더링을 하지않는 ref 대신에 useEffect를 사용해서 의존성배열을 빈배열로 두어 첫 렌더링에만 포커스가 잡힐 수 있게 한다.
그렇다면 id 인풋에 입력값이 10자가 넘으면 포커스가 password로 잡히게 해보자
우선 input의 입력값을 가져오기 위해 state를 선언해주고
const [id, setId] =useState("")
state가 인풋이랑 엮일 때 꼭들어가는 value랑 onchange를 넣어준다.
const idOnchage= (event)=>setId(event.target.value)
<div>
아이디 :<input
type='text'
ref={idRef}
value={id}
onChange={idOnchage}
/>
</div>
그리고 useEffect를 이용해서 id 인풋의 입력값이 10초과 일때만 리렌더링 되어 passward로 포커스가 넘어가도록 하려면
useEffect(()=>{
if(id.length>=10){
passRef.current.focus()
}
},[id])
useEffect의 메인로직에 if문을 이용해 state로 선언해준 id의 length가 >=10일 때 포커스가 잡히게 해라 해두고 의존성 배열에 id를 넣어두면 id의 변화 있을 때 계속 렌더링을 하다가 id의 입력값이 10이 되었을 때 포커스가 옮겨간다.
콘솔이 맨처음 이랑 아이디의 인풋값이 변동될때만 쌓이는 것을 확인 할 수 있다.
'React' 카테고리의 다른 글
component LifeCycle 생명주기 (1) | 2023.11.09 |
---|---|
React Hooks useContext, reactMemo, useCallback, useMemo (1) | 2023.11.09 |
GlobalStyle, sass, css초기화 (0) | 2023.11.08 |
styled component 작성법 장점 자바스크립트 메소드 사용 (0) | 2023.11.07 |
반복되는 컴포넌트 처리, 나누기 (0) | 2023.11.01 |