React의 얕은 복제와 깊은 복제
7782 단어 프런트엔드
React의 얕은 복제와 깊은 복제
학습 전단이 1년도 안 되어react 프레임워크에 있는 코드를 작성할 때 프로젝터 페이지가 바뀌었는데도 다시 렌더링하지 않은 문제가 자주 발생한다.그래서 react의 얕은 복제와 깊은 복제의 차이점과 실현 방식을 정리해 봤습니다. 이제 막 입문한 앞부분의 어린이 신발들에게도 도움이 되었으면 좋겠습니다.
얕은 복사
우선 우리는 아래의 예를 통해 얕은 복제가 무엇인지 이해하자.
/* */
let obj = {a: 1};
let newObj = obj;
newObj.a = 2;
console.log(obj.a);
//log 2
우리는 자바스크립트의 저장 대상이 주소만 저장하는 것을 알고 있다. 위 코드
newObj = obj
에서는obj 대상이 가리키는 주소를 newObj에 부여한다. 즉, 이 동작을 한 후에obj와 newObj가 같은 메모리 주소를 가리키기 때문에 우리는 newObj에 대해 말한다.a부치는 사실obj에 대한 것과 같다.a 값을 부여합니다.그래서 얕은 복제는 메모리 주소 하나만 복제한 것으로 이해할 수 있다.우리가 코드를 쓸 때 가장 자주 사용하는 것은 얕은 복제이다. 예를 들어 우리는 아래의 코드를 자주 쓴다.
/* */
/*......*/
this.state = {
id : 1,
value : "test"
}
/*......*/
render(
const { id, value } = this.state;
return(
<div> id: { id }, value: { value } div> ) )
여기
const { id, value } = this.state
는 사실 간단한 복제입니다. 우리는 렌더링을 할 때 변화하는 값을 제공하는 데 사용되며,render에서 어떤 변수를 다른 조작할 필요가 없습니다.따라서 명확하게 간단하게 복제된 상황에서 가능한 한 const 정의 변수를 사용한다. 왜냐하면 const 정의의 변수는 값을 부여하거나 변경할 수 없기 때문에 뒤에서 이 변수를 부주의로 바꾸어서 발생하는 문제를 피할 수 있다.얕은 복제의 장점은 메모리 주소를 효과적으로 절약하고 불필요한 메모리 낭비를 피할 수 있다는 것이다.코드 1로 돌아가서 obj에만 메모리 주소를 복사하지 않으려면 newObj에 새 메모리 주소를 할당하고 값을 부여해야 합니다.
/* */
let obj = {a: 1};
// newObj
let newObj = {};
//
newObj = obj;
newObj.a = 2;
console.log(obj.a);
//log 1,obj
이것은 심도 있는 복제로 볼 수 있다. 우리는 새로운 대상과 새로운 메모리 주소를 얻었고 이후에 newObj에 대한 조작이obj에 영향을 주지 않기 때문에 대담하고 안심할 수 있는 조작이다.
딥 클로닝
코드 3의 예는 명백한 깊이 있는 복제로 메모리 주소를 분배한 다음에 값을 부여하는 것이다.이런 방법을 제외하고 우리는 es6의 Object를 사용할 수 있다.assign () 방법은 깊은 복제를 실현합니다.
/* */
let obj = {a: 1, b:{ c: 2}};
let newObj = Object.assign({},obj);
newObj.a = 2;
newObj.b.c = 3;
console.log(obj.a);
//log 1,obj
console.log(obj.b.c)
//log 3,obj
newObj에 대한a 할당 후 obj는 변하지 않았지만 newObj에 대해서는 변경되지 않았습니다.b.c가 값을 부여한 후에obj의 값이 바뀌었다.이건 Object 때문이야.assign () 방법은 1층의 변수만 깊이 복제할 수 있기 때문에 2층은 사실 얕은 복제이다.우리는obj에게.b도 Object를 한 번 씁니다.assign () 만이 완전한 심도 복제를 완성할 수 있습니다.만약 obj 안에 많은 층이 있다면, 순환 삽입은 Object를 사용해야 한다.assign () 방법은 여러 번 있습니다.코드 3의 방법도 한 층만 깊이 복제할 수 있다.
ES7에서는...을 사용하여 객체의 구조를 해제하고 깊게 복제해야 하는 변수를 복제할 수 있습니다.
/* */
let obj = {a:1,b:2};
let newObj = {...obj,a:2};// a
newObj.b = 3;// b
console.log(obj);
//{a:1,b:3}
console.log(newObj);
//{a:2,b:3}
Reducer에서 자주 이런 방식으로 우리state를 업데이트하여 다시 과장하는 목적을 달성합니다.
/* */
const initialState = {
count: 1,
value: { name : "Michael" }
}
function reducer(state = initialState, action){
switch(action.type){
case ADD_COUNT:
return { ...state, count: state.count + 1 };
case CHANGE_NAME:
return { ...state, value : { name : "Lyle" } }
default:
return state;
}
}
state & props
state를 바꾸려면 setState () 방법을 사용해야 합니다. setState () 방법은 심복제에 속합니다. 가장 자주 사용하는 쓰기 방법은
/* */
this.state = {
value: { a: 1 }
}
const { value } = this.state;//
value.a = 2;
console.log(this.state.value.a);// 2, dom
this.setState({ value });//dom
여기value.a=2 state에서value의 값이 바뀌었지만, 얕은 복사이기 때문에, 신구value는 같은 메모리 주소를 가리키며, 구성 요소가 업데이트될 때 (state,props가 바뀔 때) 기본적으로 신구 대상의 메모리 주소가 일치하는지, 일치하지 않으면 업데이트하지 않습니다.같은 이치로, 만약 Reducer에서 현재state를 직접 수정하고 프로포즈를 되돌려준다면, 해당 프로포즈를 호출하는 구성 요소는 렌더링을 업데이트하지 않습니다.
/* */
const initialState = {
count: 1
}
function reducer(state = initialState, action){
switch(action.type){
case ADD_COUNT:
state.count += 1;
return { ...state }; //state , state
default:
return state;
}
}
구성 요소 업데이트의 원리를 바탕으로 신구state나props가 같은 메모리 주소를 가리키는지, 그렇지 않으면 업데이트하지 않는지 비교합니다.두 대상의 값이 같지만 같은 주소를 가리키지 않아도dom는 다시 렌더링된다는 것이다.이것은 우리가 보고 싶은 것이 아니다. 프로포즈의 값이 바뀔 때dom를 다시 렌더링하는 것이 필요하다.shouldComponentUpdate () 방법에서dom의 업데이트 여부를 정의할 수 있습니다. 예를 들어if (props==nextProps)returntrue입니다.
완전한 딥 클로닝
앞서 언급한 심층 복제는 기본적으로 한 층의 변수만 복제할 수 있거나, 반드시 여러 층의 변수를 끼워 넣어야 한다.대상을 더욱 편리하게 완전히 복제하려면 다음과 같은 방법을 사용할 수 있습니다.
1. JSON을 사용합니다.stringify() 및 parse() 방법:
const newValue = JSON.parse(JSON.stringify(value));
2.lodash —— .clone(obj, true)/.cloneDeep(obj)
총결산
1. Reducer에서state를 업데이트할 때 매번 새로운(심복제) 대상을 되돌려 주는 것을 확보한다.2. 자주 사용하는 구성 요소에서 shouldComponentUpdate() 방법을 사용하여 렌더링 효율을 높인다.