Notice
Recent Posts
Recent Comments
«   2024/07   »
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 31
Tags more
Archives
Today
Total
관리 메뉴

HYEWON JUNG의 개발일지

useState, useEffect, useRef 본문

React

useState, useEffect, useRef

혜won 2023. 11. 8. 21:46

useState

가장 기본적인 react hook 이며 함수형 컴포넌트 내에서 가변상태를 갖게 한다. 

const [state, setState] = useState(초기값);

이런 기본 형태에서 리렌더링을 하려면  setState를 이용해서 해야하는데 전에 count를했었는데 당시에 사용한 방식은 

2023.11.01 - [React] - state , state와 불변성, 전개구문을 이용한 불변성 유지

 

state , state와 불변성, 전개구문을 이용한 불변성 유지

state를 쓰는 이유 UI를 바꾸기 위해서다, 렌더링을 다시하기 위해서 const [state, setState]= useState('initial Value') 배열 안의 'state'는 초기값을 받을 변수, 'setState'는 변수를 제어할 메소드 , 'initial Value'

hyewonjung-coding.tistory.com

<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를 해주면 어떻게 될까

인풋의 값이 바뀔 때마다 콘솔이 찍히는 것을 볼 수 있다. 

이 로직이 실행되는 순서는 

  1. input에 값을 입력한다.
  2. value, 즉 state가 변경된다.
  3. state가 변경되었기 때문에, App 컴포넌트가 리렌더링 된다.
  4. 리렌더링이 되었기 때문에 useEffect가 다시 실행된다.
  5. 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이 되었을 때 포커스가 옮겨간다.  

콘솔이 맨처음 이랑 아이디의 인풋값이 변동될때만 쌓이는 것을 확인 할 수 있다.