어쩌다 알게 된 ƪ(•̃͡•̃͡ ƪ

🟦[React] useState와 useImmer의 차이 본문

개발/🟦 React

🟦[React] useState와 useImmer의 차이

비니_ 2025. 6. 6. 15:33
728x90

 


항목 useState useImmer
문법 복잡도 복사 필요 (...prev) 단순 (직접 수정처럼 작성 가능)
상태 불변성 관리 수동 자동 (immer 내부에서 처리)
성능 얕은 비교에 적합, 구조가 단순할 때 유리 구조가 복잡한 상태에도 유지 관리 쉬움
라이브러리 필요 여부 ❌ 기본 내장 ✅ immer 설치 필요
 
import { useState } from 'react';
import { useImmer } from 'use-immer';


/*
    // useImmer와 useState의 공통점
        - 상태 관리 훅
        - 불변성 유지
    // useImmer와 useState의 차이
        ** useImmer => 직접 수정하듯 작성 가능 (직접 상태를 변경하는 것 처럼 보이지만, 안보이는 내부적으로는 복사본을 만들어서 수정하고 새 상태로 자동으로 변경해줌)
        ** useState => 직접 복사해서 수정한 새 객체 반환 (복사할수록 중첩, ...prev 같은 형태)
*/

export default function ImmerUse(){
    const [state, setState] = useImmer({ count: 0 });
    const [state2, setState2] = useState({ count: 0 });

    return(
        <>
            <div>{state.count}</div>
            <button onClick={() => setState(prev => { prev.count += 1})}> +1 </button>

            <div>{state2.count}</div>
            <button
                onClick={() =>
                    setState2(prev => ({
                        ...prev, // {count: 0}이 아니고 그냥 (0)이었으면 필요없음
                        count: prev.count + 1
                    }))
                }> +1 </button>
        </>
    )
}

 

 

 

내가 이해한 것

이 두개가 동작은 같은데 내부에서 이루어지는 상태가 다르다

setImmer는 복사를 안하고 직접 렌더링하는 것 처럼 보이지만 안보이는 곳에서 복사를 하고 새 상태로 만들어 자동으로 채워준다

useState는 계속 직접 복사를 하면서 중첩으로 관리를 한다

 

 

 

예시

import { useState } from 'react';
import { useImmer } from 'use-immer';

// useState 버전
export default function Scoreboard() {
    const [player, setPlayer] = useState({
        firstName: 'Ranjani',
        lastName: 'Shettar',
        score: 10,
    });

    function handlePlusClick() {
        setPlayer({
        ...player,
        score: player.score + 1,
        })
    }

    function handleFirstNameChange(e) {
        setPlayer({
        ...player,
        firstName: e.target.value,
        });
    }

    function handleLastNameChange(e) {
        setPlayer({
        lastName: e.target.value
        });
    }

    return (
        <>
            <label>
                Score: <b>{player.score}</b>
                {' '}
                <button onClick={handlePlusClick}>
                +1
                </button>
            </label>
            <label>
                First name:
                <input
                value={player.firstName}
                onChange={handleFirstNameChange}
                />
            </label>
            <label>
                Last name:
                <input
                value={player.lastName}
                onChange={handleLastNameChange}
                />
            </label>
        </>
    );
};

// useImmer 버전
export function ScoreboardImmer() {
    const [player, setPlayer] = useImmer({
        firstName: 'Ranjani',
        lastName: 'Shettar',
        score: 10,
    });

    function handlePlusClick() {
        setPlayer(draft => {
            draft.score += 1;
        })
    }

    function handleFirstNameChange(e) {
        setPlayer(draft => {
            draft.firstName = e.target.value;
        })
    }

    function handleLastNameChange(e) {
        setPlayer(draft => {
            draft.lastName = e.target.value;
        })
    }

    return (
        <>
            <label>
                Score: <b>{player.score}</b>
                {' '}
                <button onClick={handlePlusClick}>
                +1
                </button>
            </label>
            <label>
                First name:
                <input
                value={player.firstName}
                onChange={handleFirstNameChange}
                />
            </label>
            <label>
                Last name:
                <input
                value={player.lastName}
                onChange={handleLastNameChange}
                />
            </label>
        </>
    );
}

728x90
Comments