[TIL] 불변성 관리

Kyeong_Bong·2022년 6월 21일
1

TIL

목록 보기
11/18
post-thumbnail

리액트의 상태가 불변성 관리를 하는 이유는 크게 두가지이다.

  1. 값이 변경됐는지 알기 위해서
  2. 값을 수정하면, 이전 상태와 바뀐 상태값이 동일해져서 비교할 수 없어서

리액트에서는 상태가 변하면 컴포넌트가 리렌더링된다.

중요‼️
리액트에서 리렌더링할 때 값 자체가 아니라 참조 값을 비교하므로 참조 값이 동일하면 변경을 감지할 수 없다.
값을 수정하면 이전 상태와 바뀐 상태값이 동일해지기 때문에 이전 상태와 현재 상태를 비교하여 렌더링할 수 없게 된다.

불변성을 관리하는 방법

  1. assign 메서드

  2. spread 연산자

  3. 라이브러리

    assign

    Object.assign(target, ...sources)

    첫 번째 인자는 타겟, 두 번째 인자부터 마지막 인자까지는 소스 오브젝트이다.
    소스 오브젝트는 타겟 오브젝트에 병합되고 리턴값으로 타겟오브젝트를 반환한다.

    예시1
     const one = {a:1};
     const arr = Object.assign({}, one);
     console.log(arr); // {a: 1}
    예시2
     const one = {a:1};
     const two = {b:2};
     const three = {c:3};
     const arr = Object.assign({}, one, two, three);
     console.log(arr); // {a: 1, b: 2, c: 3}

    spread

    // 어떠한 인수를 전달하고 싶을때 원래 사용하는 방식은 다음과 같다 //
    function showName(name){
        console.log(name);
    }
    showName('Mike');           // 'Mike'
    showName('Mike', 'Tom');    // 'Mike' 첫번째 값만 찍힘 하지만 에러는 나지 않음
    showName();                 // undefined 에러는 나지 않음
    // 이처럼 값이 여러개 값이 있을경우 잘 전달하지 못한다. //
    // 나머지 매개변수 //
    // 1번 예시 [값을 단순히 전달하고 싶을때] //
    function showName(...names){
      console.log(names);
    }
    showName();                 // []
    showName('Mike');           // ['Mike']
    showName('Mike', 'Tom');    // ['Mike', 'Tom']
    // 나머지 매개변수 //
    // 2번 예시 [전달 받은 모든 수를 더하고싶을때] //
    function add(...numbers) {
      let answer = numbers.reduce( (arr, cur) => arr + cur );
      console.log(answer);
    }
    
    add(1, 2, 3);                          // 6
    add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);    // 55
    // 나머지 매개변수 //
    
    // **3번 예시** [user 객체를 만들어 주는 생성자 함수를 만들때] //
    
    function User(name, age, ...skills){
      this.name = name;
      this.age = age;
      this.skills = skills;
    }
    
    const user1 = new User("mike", 30, "html", "css");
    const user2 = new User("Tom", 20, "java", "spring");
    const user3 = new User("Jane", 10, "c++"); 
    
    console.log(user1); // User { name: 'mike', age: 30, skills: [ 'html', 'css' ] }
    console.log(user2); // User { name: 'Tom', age: 20, skills: [ 'java', 'spring' ] }
    console.log(user3); // User { name: 'Jane', age: 10, skills: [ 'c++' ] }
    
    // 주의할 점 //
    // 나머지 매개변수를 입력 할 때 스프레드문법이 가장 뒤에 와야한다. //
    // 그 이유는 남은 값들을 모조리 끌어다가 배열로 묶기 때문이다. //
  • 전개 구문

    // 전개 구문 : 배열 //
    
    let arr1 = [1, 2, 3];
    let arr2 = [4, 5, 6];
    
    let answer = [...arr1, ...arr2];
    
    // ...arr1 = 1,2,3
    // ...arr2 = 4,5,6      
    
    console.log(answer); // [1, 2, 3, 4, 5, 6]
    // 전개 구문 : 객체 //
    
    let user = {name : 'Mike'}
    let mike = {...name, age : 30}
    
    console.log(mike) // {name : 'Mike', age : 30}
    // 전개 구문 : 복제 //
    
    let arr = [1, 2, 3];
    let arr2 = [...arr]; // [1,2,3] 원래 배열엔 아무런 영향을 주지 않음
    
    let user = {name : "Mike", age : 30};
    let user2 = {...user};
    
    user2.name = "Tom";
    
    console.log(user);  // { name: 'Mike', age: 30 }
    console.log(user2); // { name: 'Tom', age: 30 } 
    //	원래 배열엔 아무런 영향을 주지 않고 name만 변경
    // 전개구문 : 객체 심화 //
    
    let user = { name : "Mike" };
    let info = { age : 30 };
    let skill = ["JS", "React"];
    let lang = ["Korean", "English"];
    
    user = {
    	...user,
    	...info,
    	skills : [...skill, ...lang],
    }
    
    console.log(user);
    
    // console.log(user) 값
    {
      name: 'Mike',
      age: 30,
      skills: [ 'JS', 'React', 'Korean', 'English' ]
    }

    라이브러리

    위 두 가지 방식을 사용하면 얕은 복사가 된다. 그러므로 객체 안의 객체가 있으면 그 안의 객체도 복사해주어야 한다.

    하지만 무분별하게 깊은 복사를 사용하는 것은 다음과 같은 이유로 문제가 될 수 있다.

    깊은 복사는 성능이 저하될 수 있다.
    리액트에서 변경되지 않은 객체도 변경되었다고 감지하여 모든 것을 리렌더링할 수 있다.
    그러므로, 변경된 객체만 변경해주는 것이 좋다.
    이를 편하게 하고자 immer.js와 같은 라이브러리를 사용하여 쉽게 불변성을 관리할 수 있다.

profile
Junior Developer🔥

0개의 댓글