react 불변객체를 사용해야 하는 이유

차유림·2021년 11월 17일
1

react 불변객체를 사용해야 하는 이유

위의 코드에서 첫번째 obj변경 버튼을 클릭했을 경우
객체 내부값을 변경하고 setState를 적용하지만, 재렌더링이 일어나지 않는다.
react에서는 상태가 변했는지 비교할 때, Object.is를 사용하기 때문이다.

 <button onClick={() => {
          sample.sound = "왈왈";
          setState(sample);
 }}>

두번째 obj변경-불변객체 버튼을 클릭했을 경우,
새로운 객체를 setState에 적용하기 때문에 재렌더링이 일어난다.

 <button onClick={() => {
     setState({ ...sample, name:"happy" });
 }}>

useState 특징

useState in React: A complete guide 글이 길기는 하지만, 리액트 useState에 대해 정말 자세히 나와있는 좋은 글이었다.
마지막 결론을 정리하자면 다음과 같다.

useState는 함수형 컴포넌트에서 상태값을 사용할 수 있게 해주는 Hook(function)이다.
초기상태를 전달하면, 함수는 현재상태값과 상태를 업데이트할수있는 함수를 반환한다.

  • 상태업데이트 함수는 값을 바로 업데이트하지 않는다.(비동기로 처리됨)
  • 따라서 업데이트상태에 이전 상태값을 사용한다면, 함수형태로 전달해야한다.
    setMessage(previousVal => previousVal + currentVal)
// 추가 예시
const [count, setCount] = useState(0);

// 1. 값을 사용했을 때, 1,2,3,4-- 로 변경
const onClick = () => {
  setCount(count+ 1);
  setCount(count+ 1);
  console.dir(count);
}

// 2. 함수를 사용했을 때, 2,4,6,8-- 로 변경
const onClick = () => {
  setCount(count=> count+ 1);
  setCount(count=> count+ 1);
    console.dir(count);
}
  • 현재상태와 같은 값으로 업데이트하면 리렌더링 되지 않는다. (리액트는 Object.is 로 비교하기 때문)
  • 클래스컴포넌트의 this.setState와 달리 useState는 객체를 병합(merge)하지 않고, 대체(replace)한다.
  • useState는 Hooks 규칙을 따르며, 특히top level 에서 호출되어야 함에 주의하자.

같은 값으로 상태 업데이트해도 재렌더링 되는데요?🤔

위 글에 질문을 살펴보다가, 'useState에서 같은 값으로 상태를 업데이트할 경우에도 재렌더링 됩니다~' 라는 글을 보았다.
저자의 답변은 'react 공식문서에 보니 다음과 같이 적혀있네요. 재렌더링을 항상 스킵하지는 않네요! 이부분은 업데이트하겠습니다. 감사합니다' 였다.

리액트 상태 업데이트

If you update a State Hook to the same value as the current state, React will bail out without rendering the children or firing effects. (React uses the Object.is comparison algorithm.)
Note that React may still need to render that specific component again before bailing out. That shouldn’t be a concern because React won’t unnecessarily go ‘deeper’ into the tree.

그래서 질문자가 남긴 코드샌드박스 코드를 확인해보았다. 같은값으로 업데이트할때 계속 재렌더링이 일어났다.

그러다 stack overflow 에서 다른 글 Why React needs another render to bail out state updates?을 찾아볼 수 있었는데, 같은 값을 렌더링할 때 두번째 까지는 재렌더링이 일어나고 그 후에는 안일어나는 이유가 뭔가요..? 라는 글이었다.
여기서는 매번 재렌더링이 일어나지 않고 두번째까지만 재렌더링이 일어났다. (초기값과 다를 경우, 만약 초기값과도 같다면 재렌더링은 발생하지 않는다.)

그래서 두 코드를 비교해봤지만, 차이가 없었다. 그럼 왜 이런 다른 결과가 나올까?
react 버전의 차이인것 같다. 계속 재렌더링이 일어나는 경우 리액트 버전이 16.7.0-alpha.0-next 였고, 두번만 재렌더링이 일어나는 경우는 16.8.6 버전이었다.

리액트 버전 16.8.0

리액트 버전 16.7.0알파

this.setState(클래스형) vs useState(함수형)

위에서 클래스컴포넌트의 this.setState와 달리 useState는 객체를 병합(merge)하지 않고, 대체(replace)한다는 특징이 있었다.
더 자세히 살펴보자.

리액트 공식문서에 보면 함수형에서 객체 상태값을 업데이트할 경우에는, spread 문법을 사용해 기존 객체값을 적고, 변경된 값을 같이 적어야 한다고 말한다.

this.setState(클래스형) vs useState(함수형)

클래스형에서는 this.state안에 상태값을 모두 관리하기 때문에 setState에서 변경된 상태값만 입력해도 기존 상태값에 merge되는 형태로 동작했다.
하지만 함수형에서는 setState가 merge가 아닌, 기존 값을 대체하기 때문에 기존 값도 적어주어야 한다.
마찬가지로 클래스형에서도 this.state.hello 값이 객체인 경우에는, 변경된 값만 적어주는 것이 아니라, 기존 객체 값도 spread문법으로 적어줘야 한다.

export default class Hello extends Component {
  constructor(props) {
    super(props);
    this.state = {
      hello: {
        name: 'haha',
        age: 20,
        job: '백수'
      }
    };
  }

  render() {
    const { name, age, job } = this.state.hello;
    return (
      <div>
        name: {name}
        age: {age}
        job: {job}
        <button onClick={() => this.setState({ hello: { ...this.state.hello, job: 'developer' } })}>취직</button>
      </div>
    );
  }
}
profile
🎨프론트엔드 개발자💻

0개의 댓글