라이브러리&프레임워크/React

[리액트] 리액트의 useState 상태변화, 그에 따른 비동기처리

youngble 2022. 3. 13. 17:41

리액트 상태변화

기존  변수의 값이 onClick 이건 다른 이벤트 발생해서 내용이 바뀌더래도

화면상에서는 변경된 변수로 보이지 않는다 이유는 초기 렌더링 이후에 data가 바뀌었다는걸 리액트가 평가하여 재 평가하거나 렌더링 하지 않기 때문이다. 예를들어 

let changingIt = 'a';

const onChangeSpelling => ()=>{
  changingIt = 'b'
}

이렇게 변수가 있고 어떤 특정 리스너(클릭)을 하여 'a' 문자에서 'b' 라는 문자로 만든다고 했을때 return 부분에 changingIt의 데이터를 화면에 출력되는거라고 할때,  리액트는 그런걸 신경쓰지않는다고한다. 당연히 코드는 리스너(클릭이벤트)가 발생했으니 해당함수는 실행하지만 화면변화를 위한 리액트는 이것을 무시하기때문에 컴포넌트함수는 다시 실행(렌더링)되지않는다. 따라서 화면에 바뀐 data를 업데이트하고 그 부분만 렌더링하기위해서 state라는 걸 사용한다. 이때 React 라이브러리에서 무언가를 가져오는데 이때 named import를 한다.

Import {useState} from “react”

이는 리액트 라이브러리가 제공하는 함수이며 값을 상태로 정의 할수 있도록 해주고 이런 값의 변화가 일어나면 변화된 값을 반영한 컴포넌트 함수가 다시 실행(렌더링)된다.

useState 함수는 리액트 훅들중 하나이다. 이러한 리액트 hooks 들은 react 컴포넌트 함수내에서만 불러올수있고 그 밖에서는 불러올수없다.(예외 hook? 있다곤하는데 그건 나중에)

 

useState를 사용하기위해 초기값을 useState함수 인자에 넣어주면 되는데 이때 배열형태로 두개의 값을 리턴해주게되는데 첫번째값은  현재값 이고 두번째값은 변수값을 바꿔줄 업데이트 함수다. 따라서 모던 자바스크립트의 새로운 기능 destructuring(구조분해할당) 을 사용하여 배열에 반환값을 넣어준다.

Array desctructuring 배열구조분해 , Object destructuring 객체구조분해

이때 배열구조분해로 할당할때는 대입하는 값의 배열순서만 신경쓰고 객체구조분해는 대입하는 순서와 상관없이 key 를 신경쓴다.

Const [data, SetData] =useState(초기값)

이렇게해서 화면에 보여주는 어떤값이 바뀔때 SetData함수를 사용하여 변수를 변화시킬때 리액트가 이것을 신경써서 컴포넌트를 다시 실행(리랜더링) 되게 하고 만약에 혹시 useState를 사용하는데 음악이나 다른 특정 값이 변해도 화면에 보여주지 않는 경우에는 setdata에 업데이트하지않고 그냥 변수에 = 를 사용하여 바꿔줘도 된다(단, const일땐 불가능).

 

이런상황이 많지않고 굳이 그렇게 대입할일은 없겠지만 그전에 나는 프로젝트를 진행하면서 audio 객체의 속성값 소리크기 volume, 반복재생 loop, 정지 pause 등등을 쓸때 해당 값이 바뀌면 인식은 하지만 화면에서 리랜더링 할필요없고 그 변수안에 메서드를 접근하여 그 값만 바꿔주기에 기존 audio 전체 값을 바꿔주지않기때문에 set을 하지않았다.

-> 만약 변화된 state에 따른 화면변화가 아니라면 굳이 useState변수에 만들어서 위와같이 set함수를 쓰지않고 변수에 = 로바로 바꾼다면 useState의 의도와 맞지 않다고 생각하고 절대? 그런식으로 쓰면 안된다고 생각한다 각 의도에 맞게 사용하기 때문에 state변화를 위한게 아니라면 개별 변수로 사용하는게 나을듯 싶다.

 

-> 한가지 더 생각해야할게 만약 data 라는 값이 한가지의 값을 가진게 아닌 객체 형식이라면(또는 배열이라면),

그안에 있는 값들 몇가지만 바꾸고싶다면 spread operator를 사용하여 기존 값들은 그대로 두고 나머지만 바꾸면 될거같다.

예를들어 storybook 이라는 객체로 useState를 만들었다고한다면 

const [storybook, setStorybook] = useState({a:"",b:"",c:""})

storybook 에는 여러 속성값(키값) 이 있는 object 이니깐 위에처럼 storybook.a ="" 이런식으로 재할당하지말고 spread operator를 사용하여 바꾸고자 하는 속성값만 바꾸면 될거같다. 예를들어 a값을 "cd"로 바꾸고 싶다면,

const [storybook, setStorybook] = useState({a:"",b:"",c:"",})

  const ahahah1=(event)=>{
    setStorybook((prevState)=>{return {...prevState,a:"cd"}})
  }
   return (
    <WrapBox className="App">
      <div>
        <button onClick={ahahah1} style={{ width: "100px", height: "100px" }}>
          dddd
        </button>
      </div>
    </WrapBox>
  );

이런식으로 쓰게되면 set함수의 화살표함수의 첫번째인자는 이전에 가진 최신 기존 값들을 가진 정보를 가진 snapshot 값들을 스프레드연산자를 사용할수있어서 다른 기존값들은 그대로 복제하고 a 값만 바꿀수 있다.

이때 어떤 event가 생길때 set해주게되면 잘되는데 함수를 만들지않고 밖에서 어떠한 이벤트없이 set해주게 되면 무한 loop 에러가 뜬다.

const [storybook, setStorybook] = useState({ a: "", b: "", c: "" });
  setStorybook((prevState) => {
    return { ...prevState, a: "cd" };
  });

이유는 처음 이야기한데로 useState의 상태값이 변하면 리액트가 해당 컴포넌트가 다시 실행되도록하는데 다시 실행될시 위의 코드에서 SetStorybook(()=>.....) 이부분이 또 실행된다. 위의 말한데로 초기 useState로 할당한 값은 리액트가 기억하고 추적하기때문에 실행되지 않지만 setStorybook() 함수는 생략하지 않기때문에 다시 상태값을 바꾸게 되어 무한루프에 빠지는것이다. 따라서 재실행된 컴포넌트가 무한루프가 되지않게 하기위해선 어떤 특정 조건이나 이벤트 일때 동작하도록 해야한다.

 

본론으로 돌아가서 set을 사용하여 새로운 값이 할당되었다고 리액트에게 전달하기때문에 useState가 등록된곳의 컴포넌트를 다시 실행하고 JSX 코드도 다시 evaluate 한다. 

 

이때 중요한건 만약 해당 함수 컴포넌트를 다시실행하게된다면 기존에 초기화했던 useState도 읽어와서 다시 초기값으로 돌아가지 않나 라고 생각 할수있는데 리액트가 초기 실행한것을 기억하고 추적하여 다시 실행하더래도 변경된 최신값으로 업데이트해준다고한다.

비동기처리

function testAsynchronous (){
	setData("내용을 바꿔줌");
    console.log(data) // "내용을 바꿔줌" 이라는 문자열이 출력 되지 않는다.
}

만약에 setData 하여 data값을 변화하여 리랜더링 되더래도 setdata 실행하는 함수내에서 console.log 를 사용하여 data값을 출력해보면 바뀐값이 출력되지않고 기존에 있던 값이 출력되는데 이유는 값을 바로 바꿔주는게 아니라 상태가 업데이트되도록 계획하기 때문이다.( 생각에는 비동기처리 느낌이다. 함수를 등록하고 실행되기전에 아래를 먼저 실행하기때문에 console.log 먼저 찍고 setData함수 기능이 실행되는거라고 생각한다) 그래서 새로운 값이 아직 가능하지 않게 된다. 찾아보니 비동기처리가 맞았다. 리렌더링이 된 후에야 비로소 업데이트된 state가 반영된다. ( state 변경 - 리렌더링 - state 반영 ) 순이라고 한다. <- 이건 나중에 다시 찾아볼것 블로그에서본거라

 

이렇게 기존에 이미 공부했던 state에 관해서 복습하면서 정리하였는데 Maximilian 도 말하기를 어떻게 내부에서 동작하는지 모른다면 나중에 프로젝트가 커지고 더 복잡한 react 애플리케이션에서 갑자기 생각한데로 값이 업데이트 안되는걸 마주할수있기 때문이다.