LoginSignup
0
1

More than 1 year has passed since last update.

React入門 - Tips7 - immer。オブジェクト配列のクローン

Posted at

目次

この記事を書くに至った理由

formikを使用し、繰り返し項目を使用する場面でよく分からないエラーが出て、少しはまりましたが解決しました。
結論は、formikの繰り返し項目と紐づけている定義のオブジェクト配列のクローンの仕方に問題があったようです。
(入力画面でその繰り返し項目を使用しました。確認画面へ遷移し戻るボタンで戻って来た場合に、遷移前の入力状態を復元させる部分でオブジェクト配列をクローンしているつもりが参照になっていました。それが原因で項目値をコードで変更させようとするとreadonly property的なエラーが出ていました。)

オブジェクト配列のクローン

javascriptでのオブジェクト配列のクローンは簡単には出来ないみたいですね。
ディープコピーとかシャローコピーとかそんな言葉もありますね。
ディープコピーとかクローンについては、ググってください。

試したコード

// 配列にするオブジェクト
type Test = {
    itm1: string;
    itm2: string;
}
// オブジェクト配列を含むオブジェクト(これを試します。)
type Tp = {
    tp1: string;
    tp2: string;
    test: Test[];
}

// 元ネタを初期化(値を入れておきます。)
const motoNeta: Tp = {tp1: '123', tp2: '456', test: [{itm1: '789',
itm2: '012'}, {itm1: '111', itm2: '222'}]}

// クローンできるか試すパターン
const {test, ...test2} = motoNeta; //1)元ネタをオブジェクト配列とそれ以外に分離
const slice = test.slice();  // 2)1)で分離したオブジェクト配列をslice
const spread: Test[] = [...test];  // 3)1)で分離したオブジェクト配列をスプレッド構文で展開
const assign = Object.assign([{}], test); // 4)1)で分離したオブジェクト配列にOBject.assignを使ってみる
const create = Object.create(test); // 5)1)で分離したオブジェクト配列にOBject.createを使ってみる
const jsonp: Test[] = JSON.parse(JSON.stringify(test))  // 6)1)で分離したオブジェクト配列にJSON.parse(JSON.stringify

// クローン出来ているか確認するため、test配列の一部を変更します。
test[0].itm1 = 'xxx'

// 全パターンを出力して確認します。
console.log(motoNeta)
console.log(test2)
console.log(test) //NG
console.log(slice) //NG
console.log(spread) //NG
console.log(assign) //NG
console.log(create) // NG
console.log(jsonp) //OK

出来るパターンは上記だと一つしかありませんね。
こんなサイトで試してみてください。

JSON.stringifyで展開してJSON.parseする。
しかしこのやり方は、オブジェクトの中身がstringなどに限るようです。date型などの場合、おかしくなるとどっかのサイトに書いてありました。

immer

現場にいる、他社さんに教えてもらいました。
immer。こんなのがあるんですね。
先の方法よりも簡単にクローンを作成することが出来る感じです。

ここによると

yarn add immer
でインストールし使用できるみたいです。

試したソースは以下。
こちらの場合は、
自分PCで試してもよりですが、こんなとこだとすぐ試せます。

import produce from "immer"

export default function App() {
  const baseState = [
      {
          todo: "Learn typescript",
          done: true
      },
      {
          todo: "Try immer",
          done: false
      }
  ]

  // クローン作成し、doneの値を変えてみる。
  const nextState = produce(baseState, draftState => {
      // draftState.push({todo: "Tweet about it"})  // 新しいクローンに要素追加を試す場合、コメントアウト解除してください。
      draftState[1].done = true
  })

  console.log('baseState');
  console.log(baseState);
  console.log('nextState');
  console.log(nextState);

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

immer便利かもしれない。。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1