const HabitsFu = () => {
const [habits, setHabits] = useState([
{ id: 1, name: "리액트다루기", count: 0 },
{ id: 2, name: "자바스크립트", count: 0 },
{ id: 3, name: "컴퓨터과학", count: 0 },
]);
const handleIncrement = (habit) => {
const newHabits = [...habits];
//새로운 변수에 스프레드 연산자로 얕은 복사
const dataIndex = newHabits.indexOf(habit);
//인자로 들어온 habit의 index 번호
newHabits[dataIndex].count++;
//그 인덱스 번호를 가진 복사본의 객체의 count에 ++
setHabits(newHabits);
//변경된 count 값 수정 후 복사본을 다시 전역 habits에 업데이트
};
const handleDecrement = (habit) => {
const newHabits = [...habits];
const dataIndex = newHabits.indexOf(habit);
newHabits[dataIndex].count--;
setHabits(newHabits);
};
const handleDelete = (habit) => {
const newHabits = habits.filter((item) => item.id !== habit.id);
/**삭제를 위해서 filter 함수 사용, 인자인 habit의 id와 원본 데이터의 id값이
다른 애들만 newHabits에 담긴다.**/
setHabits(newHabits);
};
}
우선 데이터는 배열이고 그 안에 객체가 있다.
즉, 참조형 데이터를 수정해야 하기 때문에 복사를 해야한다.
여기서 스프레드 연산자를 사용한다. 하지만 스프레드 연산자는 얕은 복사를 의미하며 같은 객체를 두 변수가 공유하고 있는 상태이다.
두 식별자 전역 변수 habits와 지역 변수 habits는 같은 메모리 주소값이 할당되어 있다. 그 메모리 주소를 따라가면 위의 데이터가 메모리 공간에 있다.
따라서 결국은 얕은 복사를 해도 원본을 직접 수정하는 것과 같다. 하지만 직접적인 수정은 변수를 수정하는 것이기 때문에 추후 오류와 보안 문제와 직결되지만 이는 복사함으로 이를 방지할 수 있다.
리액트에서 불변성을 지켜주는 이유는 리액트가 상태 업데이트를 하는 원리 때문입니다. 리액트는 상태값을 업데이트 할 때 얕은 비교를 수행합니다. 즉 객체의 속성 하나하나를 비교하는게 아니라 참조값만 비교하여 상태 변화를 감지합니다. 이런 이유로 배열이나 객체를 업데이트 할때 setState([...state, newState])
, setState({...state, [key]: value})
이런식으로 배열이나 객체를 새로 생성해서 새로운 참조값을 만들어서 상태를 업데이트 합니다. 이런 행위가 불변성을 지켜주는 것입니다. 불변성을 지켜줌으로써 얻게 되는 또 다른 이점은 바로 사이드 이펙트를 방지하는 것입니다. 즉 외부에 존재하는 원본데이터를 직접 수정하지 않고, 원본데이터의 복사본을 만들어서 값을 사용하기에 예상치 못한 오류를 사전에 방지할 수 있습니다. 다시 반대로 생각해보면 외부의 값을 함부로 변경할 수 있는 것은 위험한 일입니다. 만약 다른 어떤 곳에서 원본데이터를 사용하고 있다고 하면 어플리케이션 어딘가에서 사이드 이펙트가 일어날 가능성이 있기 때문입니다. 결국 리액트는 불변성을 지킴으로 인해 효과적인 상태 업데이트와 사이드 이펙트를 방지하는 이점들을 얻고 있습니다.
얕은 비교란 객체의 프로퍼티를 하나하나 다 비교하지 않고, 객체의 참조 주소값만 변경되었는지 확인합니다. 얕은 비교는 계산 리소스를 줄여주기 때문에 리액트는 효율적으로 상태를 업데이트 할 수 있습니다.
원시타입은 애시당초 불변성 특징을 가지고 있지만 참조타입인 객체나 배열의 경우 새로운 값을 변경할 때 원본 데이터가 변경이 됩니다(불변성이 지켜지지 않습니다). 이렇게 원본 데이터가 변경될 경우, 이 원본데이터를 참조하고 있는 다른 객체에서 예상치 못한 오류가 발생할 수 있습니다. 프로그래밍의 복잡도도 올라갑니다.