state에 담긴 데이터가 array, object 데이터일 경우 state 수정할 때 약간 귀찮은 점을 알아보자.
버튼을 눌렀을때 state에 담긴 array 자료 중 첫번째 데이터 '자바스크립트의 역사' -> '자바스크립트의 this' 로 변경하고 싶다.
function App(){
let [title, setTitle] = useState( ['자바스크립트의 역사', '리액트 useState', '프로그래밍 공부방법'] );
return (
<button onClick={()=>{
title[0] = '자바스크립트의 this';
setTitle(title)
}}> 수정버튼 </button>
)
}
이래도 된다. (제대로 동작은 안됨)
array 자료안의 X번째 항목을 변경하고 싶으면
array자료[X] = '바꿀값' 이렇게 하면 된다.
그래서 자료를 바꾸고 저기 state변경함수에 집어넣은 거다.
근데 이런거보다 더 나은 방법이 하나 있는데
function App(){
let [title, setTitle] = useState( ['자바스크립트의 역사', '리액트 useState', '프로그래밍 공부방법'] );
return (
<button onClick={()=>{
let copy = title;
copy[0] = '자바스크립트의 this';
setTitle(copy)
}}> 수정버튼 </button>
)
}
array, object 자료 다룰 때는 원본 데이터를 직접 조작하는 것 보다는
기존값은 보존해주는 식으로 코드짜는게 좋은 관습이다.
왜냐하면 원본 데이터를 막 바꿔버렸을 때 갑자기 원본 데이터가 필요해질 경우가 있을 수도 있다.
그래서 let copy 같은 변수에다가 기존 array를 복사해놓고
그걸 조작하는 식으로 코드짜면 조금 더 안전하다.
그런데 이렇게 작성해도 제대로 동작을 안한다.
function App(){
let [title, setTitle] = useState( ['자바스크립트의 역사', '리액트 useState', '프로그래밍 공부방법'] );
return (
<button onClick={()=>{
let copy = [...title];
copy[0] = '자바스크립트의 this';
setTitle(copy)
}}> 수정버튼 </button>
)
}
이렇게 해야 제대로 동작한다. 이렇게 해야 제대로 동작하는 이유는 state 변경함수 동작원리에 있다.
state 변경함수 동작원리
state 변경함수를 쓸 때
기존state === 신규state 이렇게 먼저 검사해본다.
그래서 같으면 state 변경을 해주지 않는다.
function App(){
let [title, setTitle] = useState( ['자바스크립트의 역사', '리액트 useState', '프로그래밍 공부방법'] );
return (
<button onClick={()=>{
let copy = title;
copy[0] = '자바스크립트의 this';
setTitle(copy)
}}> 수정버튼 </button>
)
}
그래서 위 코드에서도 setTitle(copy) 해도
copy라는 변수가 기존state와 같아서 변경을 안해준 것이다.
copy라는 변수랑 기존 state랑 안에 있는 자료가 다른데 왜 같다고 하는걸까?
기존 state는 '자바스크립의 역사'
copy에는 '자바스크립트의 this'가 들어있지만
실은 기존state === copy 비교해보면 같다고 나온다.
왜냐하면 array는 참조자료형 이기 때문이다.
array/object 동작원리
1. 자바스크립트는 array/object 데이터를 하나 만들면
예를 들어서 let arr = [1,2,3] 이렇게 만들면
[1,2,3] 자료는 램이라는 가상공간에 몰래 저장이 되고
let arr 변수엔 그 자료가 어디있는지 가리키는 화살표(메모리 주소)만 담겨있다.
2. 그래서 array/object 자료를 복사하면 이상한 일이 일어나는데
예를 들면
let data = [1,2,3];
let copy = data; //복사문법임
이런 식으로 사용하면 복사가 된다.
data에 있던 데이터를 copy에 복사한다는 뜻이다.
그럼 copy 출력해보면 [1,2,3] 이게 잘 나온다.
근데 data과 copy는 각각 [1,2,3]을 별개로 저장하는게 아니라
data과 copy는 똑같은 값을 공유한다.
data를 변경하면 copy도 자동으로 변경된다.
왜냐하면 array, object를 담은 변수에는 화살표(메모리 주소)만 저장되기 때문이다.
그래서 위 코드는 화살표(메모리 주소)를 복사한 것이다.
그래서 data, copy는 똑같은 화살표(메모리 주소)를 가지게 된다. 즉, 같은 자료를 가르킨다.
3. 그래서 같은 화살표(메모리 주소)를 가지고 있는 변수끼리는 등호로 비교해도 똑같다고 나온다.
let data = [1,2,3];
let copy = data; // 복사
copy[0] = 1000; // copy 내부 변경
console.log(copy === data) // true
자세한건 javascript reference data type이라고 검색해보면 추가학습 가능하다.
아니면 아래 포스팅을 한번 읽어봐도 좋다.
2023.03.05 - [Front end/JavaScript] - 참조, 얕은복사, 깊은복사
참조, 얕은복사, 깊은복사
오늘은 얕은복사와 깊은복사의 차이점에 대해 알아보려고 한다. 우선 얕은복사와 깊은복사의에 대해 알아보기 전에 참조에 관한 개념을 이해해야한다. 참조란? let 강동욱 = { name: '강동욱', age: 2
dongwookit.tistory.com
let copy = title;
copy[0] = '자바스크립트의 this';
setTitle(copy)
그래서 아까처럼 이렇게하면
컴퓨터는 copy와 기존 글제목 state는 똑같다고 생각하기 때문에 (화살표가 똑같아서)
state 변경을 안해준다.
let copy = [...title];
copy[0] = '자바스크립트의 this';
setTitle(copy)
이러면 잘된다. 화살표가 달라지는 문법이라 그렇다.
spread operator 라고하는 문법인데
array나 object 자료형 왼쪽에 붙일 수 있으며
뜻은 별거없고 괄호를 벗겨주세요~ 라는 뜻이다.
...[1,2,3] 이렇게 쓰면
그 자리에 1,2,3 이 남는다. 걍 괄호 벗기기용 연산자이다.
근데 두번째 용도도 있는데 array나 object 자료형을 복사할 때 많이 사용한다.
let data = [1,2,3];
let copy = [...data];
console.log(data === copy) // false
그냥 data에 있던 자료들을 괄호 벗긴담에 다시 array로 만들어주세요~ 라고 사용하면
화살표가 달라진다. 새로운 array로 인식한다.
그래서 그렇게 하면 화살표가 다른 완전 독립적인 array 복사본을 생성해줄 수 있다.
object 자료형도 마찬가지이다.
그리고 위처럼 만든 독립적인 사본을 shallow copy(얕은 복사) 라 한다.
🌈 정리
리액트에서 array/object state를 수정하고 싶으면 독립적인 카피본을 만들어서 수정하는게 좋다.
[...기존state]
{...기존state}
이렇게 하면 독립적인 카피가 하나 생성된다.
'Front end > React' 카테고리의 다른 글
가독성을 위한 JSX (0) | 2023.04.08 |
---|---|
첫 리액트 컴포넌트(아직은 Class) (0) | 2023.04.08 |
리액트 useState (2) (0) | 2023.04.06 |
리액트 useState (1) (0) | 2023.03.31 |
리액트 JSX 문법 (0) | 2023.03.30 |