[ReactJS] Immutability


Immutability


  • 不変性:値またはステータスを変更できないことを示す値

  • 状態を変えて、状態を変えないで欲しい状態を変えて、これは言いにくいです...

  • 必要な値を変換して使用する場合は、値のコピーを作成する必要があります.
  • 💡1.探すきっかけ

  • Reduxの例を学習したときに感じたことは、ステータス値の変更が非常に複雑です.(同じ機能を実行するコードで、再構築の手順を説明しています.)
  • 反応器とJavaScriptを初めて使った人には、ちょっと面倒な感じがします.
  • ステータス値をコピーして
  • に渡す理由を特定します.
  • // 리듀서
    function todos(state = initialState, action) {
        switch (action.type) {
            case CHANGE_INPUT:
                return {
                    ...state,
                    input: action.input
                };
            case INSERT:
                return {
                    ...state,
                    todos: state.todos.concat(action.todo)
                };
            case TOGGLE:
                return {
                    ...state,
                    todos: state.todos.map(todo =>
                        todo.id === action.id ? { ...todo, done: !todo.done } : todo
                    )
                };
            case REMOVE:
                return {
                    ...state,
                    todos: state.todos.filter(todo => todo.id !== action.id )
                };
            default:
                return state;
        }
    }
    // 리듀서
    // redux-actions 라이브러리 사용
    const todos = handleActions({
        [INSERT]: (state,action) => ({...state, todos: state.todos.concat(action.payload)}),
        [CHANGE_INPUT]: (state,action) => ({...state, input: action.payload}),
        [REMOVE]: (state,action) => ({...state, todos: state.todos.filter((todo) => (todo.id !== action.payload))}),
        [TOGGLE]: (state,{payload: id}) => ({...state, todos: state.todos.map((todo) => todo.id === id ? {...todo,done: !todo.done} : todo)})
    }, initialState)
    // 리듀서
    // immer 사용
    const todos = handleActions(
        {
            [CHANGE_INPUT]: (state, { payload: input }) => 
                produce(state, draft => {
                    draft.input = input;
                }),
            [INSERT]: (state, { payload: todo }) => 
                produce(state, draft => {
                    draft.todos.push(todo);
                }),
            [TOGGLE]: (state, { payload: id }) => 
                produce(state, draft => {
                    const todo = draft.todos.find(todo => todo.id === id);
                    todo.done = !todo.done;
                }),
            [REMOVE]: (state, { payload: id }) =>
                produce(state, draft => {
                    const index = draft.todos.findIndex(todo => todo.id === id);
                    draft.todos.splice(index, 1);
                })
        },
        initialState,
    );

    💡2.Reactのデフォルト属性


  • リアクターは浅い比較で新しい値かどうかを判断し、新しい値であれば再レンダリングします.

  • 浅い比較とは、オブジェクト、配列、関数などの参照タイプの実際の内部値を比較するのではなく、同じ参照(同じメモリ値を使用するかどうか)を比較することです.

  • 次のシーンを見て、なぜ反応器でstate値を直接変更できないのかを感じてみましょう.
  • 構成部品の内部に、アレイタイプのステータス値を再レンダリングする必要がある場合があるとします.
  • このとき、state.push('a')を介してステータス配列に直接アクセスして要素を追加します.
  • push値とは異なる値と考えられるが、stateとして反応する値は新しい参照値ではないため、以前と同じ値としてレンダリングされることはない.

  • 上記の理由から、新しいオブジェクトまたは配列を作成して新しい参照値を作成し、反応器に以前とは異なる参照値であることを通知する必要があります.
  • 2-1)複製オブジェクト(配列)例


    const user = { name: 'chichi', age: 30 } 
    const copyUser = user; // 배열의 복사가 아니라 같은 참조 값을 가짐
    user.age += 1; 
    console.log('user: ', user);
    console.log('copyUser: ', copyUser);
    
    /* 
    user: { name: 'chichi', age: 31 } 
    copyUser: { name: 'chichi', age: 31 } 
    */ 
    const arr = ['b', 'c', 'd'];
    const copyArr = arr;
    arr.push('a');
    console.log('arr: ', arr);
    console.log('copyArr: ', copyArr);
    
    /* 
    arr: ["b", "c", "d", "a"]
    copyArr:["b", "c", "d", "a"]
    */ 

    💡3.なぜReactで不変性を保つのか

  • 副作用防止とプログラミング構造の簡素化
  • 変数が変更できない場合、関数に副作用が発生する確率が低下します.=>プログラムの複雑さが低下する.
  • で変更できない変数はthread-safeであるため、同期問題では自由である.(2つのスレッドが同じ変数を同時に変更しようとする場合)
  • の変更が発生したオブジェクトのアドレスのみを比較することにより、Reactで最適化を行うことができる.
  • オブジェクトの内部要素を1つずつ比較する方法は効率的ではありません.
  • を使用して浅い比較を行います.
  • 💡4.不変性を保ち、状態を変える


    -アレイに追加

    setUsers(state.array.concat(user));

    -アレイから削除

    const onRemove = id => {
      // user.id 가 id 인 것을 제거
      setUsers(users.filter(user => user.id !== id));
    };

    -アレイで変更

    const onToggle = id => {
      setUsers(
        users.map(user =>
          user.id === id ? { ...user, active: !user.active } : user
        )
      );
    };

    -オブジェクトから追加

    setState(state => {...state, key: value})

    -オブジェクトから削除

    setState(state => {..._.omit(state, 'deleteKey')})

    -オブジェクトでの変更

    setState(state => {...state, key: newValue})

    - immer

  • 正式な書類
  • 2produce関数を覚えるだけでいいです.
  • の2つのパラメータを受け入れます.
  • 最初のパラメータは、変更するオブジェクト(配列)
  • です.
    パラメータをダブルクリックすると、割り当てられたオブジェクト(配列)の関数が変更されます.
    import produce from "immer";
    
    const baseState = [
      {
        todo: "javascript 공부",
        done: true
      },
      {
        todo: "Algorithm 공부",
        done: false
      }
    ];
    
    const nextState = produce(baseState, draftState => {
      draftState.push({ todo: "CS 공부" });
      draftState[1].done = true;
    });

    -reduxからimmerに書き込む

  • immer書き込み前reduceコード
  • const initialState = [{ name: "chichi", address: { city: "seoul" } }];
    
    export default function auth(state = initialState, action) {
      switch (action.type) {
        case SET_INFO:
          return {
            ...state,
            name: "sh",
            address: {
              ...state.address,
              city: "busan"
            }
          };
        default:
          return state;
      }
    }
  • immerが書いたreduceコード
  • const initialState = [{ name: "chichi", address: { city: "seoul" } }];
    
    export default function auth(state = initialState, action) {
      produce(state, draft => {
        switch (action.type) {
          case SET_INFO:
            draft[0].name = action.data.name;
            draft[0].address.city = action.data.city;
            break;
          case ADD_INFO:
            draft.push({ name: "sh", address: { city: "busan" } });
          default:
            return draft;
        }
      });
    }

    💡5.整理

  • 不変性とは、メモリ領域の値が変更できないことを意味します.
  • 不変性規則を遵守することにより、副作用を防止し、プログラミング構造を簡素化する.そして有効な状態更新を行います.
  • 不変性を有する元のタイプとは異なり、参照タイプは不変性を維持することを意図すべきである.
  • リファレンス
  • https://junwoo45.github.io/2019-11-04-memory_model/
  • https://kyounghwan01.github.io/blog/React/immer-js/#%E1%84%87%E1%85%AE%E1%86%AF%E1%84%87%E1%85%A7%E1%86%AB%E1%84%89%E1%85%A5%E1%86%BC-%E1%84%8C%E1%85%B5%E1%84%8F%E1%85%B5%E1%84%86%E1%85%A7%E1%86%AB%E1%84%89%E1%85%A5-state-%E1%84%87%E1%85%A1%E1%84%81%E1%85%AE%E1%84%80%E1%85%B5