Reactを使っていると、useState
を使用する際にcannot assign to read only property
というエラーに遭遇することがあります。このエラーは、特にオブジェクトや配列の状態を更新しようとしたときに頻発します。
この記事ではcannot assign to read only property
エラーについて、以下の内容をサンプルコードを用いてわかりやすく解説します。
cannot assign to read only property
エラーの原因cannot assign to read only property
エラーの解決方法
cannot assign to read only propertyエラーの原因
cannot assign to read only property
エラーはuseState
で管理している状態オブジェクトや配列を直接変更しようとした場合に発生します。Reactでは、状態(state)はイミュータブル(不変性)を保つ必要があります。
例えば、以下のサンプルコードはcannot assign to read only property
エラーが発生します。
import React, { useState } from 'react';
function App() {
// 初期状態を Object.freeze() で凍結(これがないとエラーがでない)
const [state, setState] = useState(Object.freeze({ count: 0 }));
const handleClick = () => {
state.count += 1; // オブジェクトを直接変更しようとする
setState(state); // エラー発生
};
return (
<div>
<p>{state.count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
export default App;
上記のサンプルコードでは、useState
で管理している状態state
を直接変更してからsetState
を呼び出しているため、エラーが発生します。
cannot assign to read only propertyエラーの解決方法
cannot assign to read only property
エラーを解決するには、useState
の状態更新時に元の状態を変更せず、新しいオブジェクトや配列を作成して状態を更新する必要があります。
以下にサンプルコードを示します。
import React, { useState } from 'react';
function App() {
// 初期状態を Object.freeze() で凍結
const [state, setState] = useState(Object.freeze({ count: 0 }));
const handleClick = () => {
// 元の状態をコピーして新しい変数を作成
const newState = { ...state };
newState.count += 1; // 必要なプロパティを更新
setState(newState); // 新しい状態をセット
};
return (
<div>
<p>{state.count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
export default App;
上記のサンプルコードでは、スプレッド構文(…state
)を使って、元の状態を新しいオブジェクトnewState
にコピーしています。その後、必要なプロパティだけを更新してをsetState
に渡しています。
以下に示すようにsetState
関数の引数に「関数」を渡す方法(setState((prevState) => {...})
)でもOKです。
import React, { useState } from 'react';
function App() {
// 初期状態を Object.freeze() で凍結
const [state, setState] = useState(Object.freeze({ count: 0 }));
const handleClick = () => {
setState((prevState) => ({
...prevState, // 元の状態を展開
count: prevState.count + 1, // 必要なプロパティのみ更新
}));
};
return (
<div>
<p>{state.count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
export default App;
配列の場合
次にuseState
で管理している状態が配列の場合において、cannot assign to read only property
エラーが発生します。
import React, { useState } from 'react';
function App() {
// 初期状態を Object.freeze() で凍結
const [items, setItems] = useState(() => Object.freeze([1, 2, 3]));
const handleAddItem = () => {
items.push(4); // 配列を直接変更
setItems(items); // 直接変更した配列をセット
};
return (
<div>
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
<button onClick={handleAddItem}>Add Item</button>
</div>
);
}
export default App;
上記のサンプルコードでは、items.push(4)
で配列を直接変更しているため、cannot assign to read only property
エラーが発生します。
解決するには配列を直接変更せず、新しい配列を作成して更新します。
import React, { useState } from 'react';
function App() {
// 初期状態を Object.freeze() で凍結
const [items, setItems] = useState(() => Object.freeze([1, 2, 3]));
const handleAddItem = () => {
const newItems = [...items, 4]; // 配列をコピーして新しい配列を作成
setItems(newItems); // 新しい配列をセット
};
return (
<div>
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
<button onClick={handleAddItem}>Add Item</button>
</div>
);
}
export default App;
以下に示すようにsetState
関数の引数に「関数」を渡す方法(setState((prevState) => {...})
)でもOKです。
import React, { useState } from 'react';
function App() {
// 初期状態を Object.freeze() で凍結
const [items, setItems] = useState(() => Object.freeze([1, 2, 3]));
const handleAddItem = () => {
setItems((prevItems) => [...prevItems, 4]); // 元の配列を展開して新しい配列を作成
};
return (
<div>
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
<button onClick={handleAddItem}>Add Item</button>
</div>
);
}
export default App;
本記事のまとめ
この記事ではcannot assign to read only property
エラーについて、以下の内容を説明しました。
- エラーの原因
- Reactの
useState
で管理しているオブジェクトや配列の状態を直接変更した場合に発生する- 例: オブジェクトのプロパティを直接変更(
state.count += 1
) - 例: 配列を直接変更(
items.push(4)
)
- 例: オブジェクトのプロパティを直接変更(
- Reactの
- エラーの解決方法
- イミュータブル(不変性)を保つ
- 状態を更新する際は、元の状態を変更せず新しいオブジェクトや配列を作成する。
お読み頂きありがとうございました。