state는 내부에서 변경 될 수 있는 값을 의미한다. 따라서 컴포넌트가 렌더링 되고 난 후 값이 변경되어 재 렌더링 될 가능성이 있는 값들은 state로 선언한다.
state는 값을 변경하는 것이 아니라 갈아 끼운다고 생각하자
이 말이 무슨 뜻이냐면,
import React from 'react';
const temp = () => {
const [name, setName] = useState("");
function settingName() {
//name = "홍길동" >> 이렇게 사용 금지!
setName("홍길동");
}
return(
// 생략
)
}
위의 코드와 같이 state값인 name을 직접적으로 바꾸지 말라는 것이다.
무조건 setName(), 변경함수를 통해 값을 바꿔야한다. 클래스 컴포넌트 형식도 마찬가지로 setState()
함수를 통하여 state값을 갱신해야한다.
경험을 얘기하자면, state 값을 직접적으로 변경한다면 렌더링이 바로바로 되지 않고 shouldComponentUpdate()
와 같은 라이프사이클 함수도 제대로 동작하지 않아 의도대로 애플리케이션이 동작하지 않는다.
앞서 state값은 변경하는 것이 아니라 갈아 끼운다고 생각하자고 필자가 적었다. 갈아 끼운다는 의미가 이 경우에 더 적합하게 적용될 것 같다. 아래의 코드를 보면
import React from 'react';
const temp = () => {
const [member, setMember] =
useState({name:"홍길동", age:25, sex:"M"});
function updateMember() {
//member.name="고길동"; >> 변경 X
let temp = member;
temp.name = "고길동";
setMember(temp);
}
return(
// 생략
)
}
member.name = "고길동"
처럼 state 값에 직접 접근하여 데이터를 변경하려 하는 것은 안된다. 그럼 주석처리가 되지 않은 코드는 잘 실행이 될까? 그렇지 않다.
깊은 복사와 얕은 복사라는 개념이 있는데, 이것을 꼭 알아야한다.✔
얕은 복사란, 위의 코드에서 let temp = member;
가 얕은 복사의 예시다.
이렇게 하면 temp도 member와 같이 동일한 객체(값){name:"홍길동", age:25, sex:"M"}
을 가진다. 하지만 레퍼런스 또한 동일한 레퍼런스를 가진다.
변수가 선언되면 메모리에 적재되는데 얕은 복사를 사용하면 두 변수가 동일한 메모리 주소를 가리키게 된다.
따라서 temp와 member는 동일한 메모리 주소를 가리키므로 temp.name="고길동"
을 하면 member.name
도 고길동으로 바뀌는 것이다.
이렇게 되면 member라는 state값을 직접적으로 바꾸는 것과 다름이 없다.
그럼 어떻게 해야할까? 깊은 복사 가 답이다 ✔
동일한 값을 가지지만 다른 메모리 주소를 가리킨다
얕은 복사와는 다르게 다른 레퍼런스를 가진다. 따라서 위의 예로 설명해보면 member를 깊은 복사하여 나온 결과가 temp라면, temp와 member는 서로 전혀 다른 객체지만 같은 값을 가진 것이다.
temp.name="고길동"
을 한다고해서member.name
의 값이 바뀌지 않는다
그렇다면 setMember(temp)가 우리가 원하는 대로 잘 동작 할 것이다.
여러 방법이 존재하지만, 필자가 자주 사용하는 것 몇가지를 소개하겠다.
let tempArray = originArray.concat(newData)
새로운 데이터를 기존 배열에 더해 새로운 배열을 만듦.
let tempArray = Array.from(originArray)
javascript의 전역 객체 Array
의 내장함수 from() 사용하여 복사본 생성
import React from 'react';
const temp = () => {
const [member, setMember] =
useState([{name:"홍길동", age:25, sex:"M"}]);
function addMember() {
let tempArray = member.concat({name:'고영희', age:25, sex:"F"});
/*tempArray ===
[{name:"홍길동", age:25, sex:"M"}, {name:'고영희', age:25, sex:"F"}]*/
setMember(tempArray);
}
function deleteMember() {
let tempArray = Array.from(member);
/*tempArray === [{name:"홍길동", age:25, sex:"M"}*/
tempArray.splice(0, 1);
setMember(tempArray);
}
return(
// 생략
)
}
let tempObj = Object.assign({}, originObj)
import React from 'react';
const temp = () => {
const [member, setMember] =
useState({name:"홍길동", age:25, sex:"M"});
function updateMember() {
let tempObj = Object.assign({}, member);
/*tempObj === {name:"홍길동", age:25, sex:"M"}*/
tempObj.name = "고길동"
setMember(tempObj);
}
return(
// 생략
)
}
Object.assign()의 경우 다른 메모리 주소를 가리키는 객체를 만들어내긴 하지만, 완벽한 깊은 복사는 아니라고 한다. 이 부분에 대해 추후에 알아볼 예정이다.