이게 왜 궁금해?

data.index.status === Status.Approval

or

const isAppoval = data.index.status === Status.Approval;

이라는 조건을 여러 곳에서 반복적으로 사용하게 되었다.

딱 봐도 길고, 반복적으로 사용하고, 여러 조건들과 함께 쓰일 경우에는 더 의미를 직관적으로 알 수 없었다.

그래서 isApproval 이라는 무언가로 만들어 쓰려고 하는데 data가 변경될 때마다 isApproval 상태도 변경되어야 했다.

그래서 그냥 변수(variable)로 선언해도 될지, setState는 사용하지 않지만, state로 선언해야할지 궁금해서 찾아보았다.

결론부터

class variable은 변경했을 때 React가 알아채지 못한다.

state는 setState로 변경했을 때 react가 변화를 감지해 re-rendering된다.

내 코드의 경우 isApproval을 내가 직접 변경하지 않고, data에 따라 변하기 때문에 변화를 감지해 리렌더링 시켜야할 일은 없다. data가 변경되었을 때 렌더링되면서 isApproval의 값도 바뀌므로 변수로 사용해도 됨!

🤷‍♀️State가 뭐지?

// class variable
export default class Test extends Component {
  render(props) {
        super(props);
    const isMe = this.props.name === 'Gisele';
    return (
      <div>
        {isMe ? '나다' : '나 아니다'}
      </div>
    );
  }
}

// state
export default class Test extends Component {
  constructor(props) {
        super(props)
    this.state = { isMe : false };
  }

  render() {
    if(this.props.name === 'Gisele') {
      this.setState({isMe: true});
    }
    return (
      <div>
        {isMe ? '나다' : '나 아니다'}
      </div>
    );
  }
}

state와 variable의 차이는 class형 컴포넌트에서 더 알기 쉬워서 가져옴.

State 는 컴포넌트의 현재 상황에 대한 정보를 나타내는 일반 자바스크립트 객체다(클래스형 컴포넌트에서). 일반 변수는 함수가 종료될 때 사라지지만, state는 값이 보존된다.

컴포넌트의 역할은 raw data를 HTML로 변환하는 것이기 때문에 이 raw data는 props와 state 객체로 구성되어 있다고 생각할 수 있다. props와 state가 render() 함수의 입력 데이터라고 할 수도 있다. 따라서 state는 구성 요소의 동작을 제어하는 일부 속성을 나타낸다.

state는 상태 변수에 변경 사항을 직접 적용하지 않는다. 이는 React에서 변경사항으로 간주되지 않기 때문이다. 대신 state의 상태는 setState() 를 사용해 변경한다. 그리고 setState는 비동기식이다. 한 줄에서 setState를 호출하고 다음 줄에서 상태가 업데이트가 된다는 보장은 없다. setState가 상태를 업데이트하라는 명령이 아니고 상태 업데이트에 대한 요청이기 때문이다. 이 문제를 해결하지 위해 업데이트가 적용된 후 실행되도록 보장되는 setState() 콜백 함수를 사용할 수 있다.

함수형 컴포넌트에서는 useState() 훅을 사용해 state를 관리한다. 클래스형 컴포넌트에서 state는 항상 객체지만, 함수형 컴포넌트에서는 모든 type이 state가 될 수 있다. 각 상태는 단일 값을 보유한다.

초기 값은 초기 렌더링에만 할당된다. 이후 렌더링에서 useState의 인수는 무시되고 현재 값이 검색된 값이 된다.

const Avartar = (user) => {
    const [userState, setUserState] = useState(user);

    // 해결
    useEffect(()=>{
        setUserState(user)
    },[user])

    return user.avartar && (<img src={user.avartar}>)
}

useState만 사용하면 해당 인수가 prop이 변경될 때마다가 아니라 처음에만 사용되기 때문에 작동하지 않는다. 이 문제를 해결하려면 들어오는 객체를 state로 사용할 때는 새 객체를 만들어야 한다.

그렇지 않으면 리액트는 객체의 변경을 감지하지 못한다.(객체는 참조형 data이기 때문에 객체 내부 값이 변해도 객체 자체의 주소값이 변하기 않기 때문)

const Message = ()=>{
    const [messageObj,setMessageObj] = useState({message:''})

    return (
        <>
           <input type='text'
                  value={messageObj.message}
                  onChange={e=>{
                  // 동작하지 않는 예시
                     messageObj.message = {e.target.value};
                     setMessage(messageObj) 

                 // 동작하려면 새로운 객체를 만들어야 한다.
                      const new = {message:e.target.value};
                      setMessage(newMessageObj)

                 // 또는 setState의 첫 번째 인수인 prevState와 스프레드를 이용할 수 있다
                       setMessage(prevState=>{
                           return {...prevState,message:e.target.value}
                       })

                }} />
        </>
    )
}

위 내용으로 state의 특징을 정리하면 다음과 같다.

  1. state는 컴포넌트의 어떤 정보를 갖고 있는 데이터다.
  2. state가 변경되면 React는 이를 감지해 render가 일어난다.
  3. state는 바로 할당하는 방식으로 변경하면 react가 변화를 감지하지 못하므로 setState를 이용해 변경한다.
  4. setState는 비동기로 처리되므로, setState 바로 다음 명령에서 새로운 값이 반영되지 않을 수 있음
  5. state를 객체로 사용할 경우 값을 할당할 때 스프레드 연산자 등을 이용해 새 객체를 만들어줘야함(객체의 data type이 참조형이기 때문에 내부값이 변해도 객체 자체의 주소값이 변하지 않기때문)
  6. setState는 첫 번째 인자로 이전 상태값을 갖는다.

그리고 내가 예전에 몰랐거나 실수를 범했던 것들의 이유를 알 수 있었다.

❓ 이 함수에서 setState를 여러번하면 렌더링이 매번 일어나나? -> 아니다. 해당 블럭이 모두 실행된 후! 렌더링이 일어난다.
❓ 객체 state가 왜 안바뀌었지? 새로운 객체를 생성하지 않고, 객체에 값을 바로 할당했기 때문
❓ 바로 위에서 setState를 했는데 왜 이전 상태값으로 처리가 됐지? 같은 함수 블럭 안에서 setState한 직후 실행한 것은 아직 state에 새로운 값이 반영되기 전이므로 그렇다. 코드 블럭이 다 실행된 후 state에 값이 반영된다.

State의 불변성


referece

'React' 카테고리의 다른 글

나만의 React 라이브러리 만들기  (0) 2022.01.18
presentational + container 패턴  (0) 2021.10.30
React Query  (0) 2021.07.09

+ Recent posts