目次
この記事を書くに至った理由
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便利かもしれない。。