暇で死にそうだったので 長文で回答させていただきます。
破壊的・非破壊的とはなにか
reactに限らずプラグラミングにおいて「破壊的操作」という単語は「メモリ参照を持つタイプの型の値を変更する際に、参照先の値を直接変更する」ことを一般的には指しています。
オブジェクトだと分かりにくいため配列で説明します。
js
1const person = { name: 'Alice', age: 25 }; // これは参照を持つオブジェクト型のオブジェクト
2const persons = [person, person]; // これは参照を持つオブジェクト型の配列
3
4// 破壊的な変更を破壊的メソッドpushで行う
5const updatedPersons = persons.push(...person);
6console.log({ updatedPersons, persons }); // => personsの値(参照)も変更されている
7
8// 破壊ではない変更を非破壊的メソッドconcatで行う
9const updatedPersons = persons.concat(persons);
10console.log({ updatedPersons, persons }); // => personsの値(参照)は変更されない
Arrayメソッド破壊・非破壊チートシート
reactにおける破壊的・非破壊的について
上記を念頭に質問者さんのコードを元に破壊的・非破壊的変更の例を確認します。
余談ですがreactでこの問題について、意味は少し異なりますがミュータブル(可変)・イミュータブル(不変)という単語が使われることが多いです。
js
1const [person, setPerson] = useState({ name: 'Alice', age: 25 });
2
3// ① 破壊的変更を行うハンドラー
4const destroyableUpdate = () => {
5 person.age = 26;
6 setPerson(person);
7};
8
9// ② 非破壊的変更を行うハンドラー
10const undestroyableUpdate = () => {
11 const newPerson = { name: 'Alice', age: 26 };
12 setPerson({ ...person, ...newPerson });
13};
14
15return (
16 <>
17 <button onClick={destroyableUpdate}>破壊的変更</button>
18 <button onClick={undestroyableUpdate}>非破壊的変更</button>
19 <p>{person.age}</p>
20 </>
21);
① 破壊的変更を行うハンドラー
この関数ではオブジェクトのプロパティを直接変更しています。
(オブジェクトAのプロパティを変更しても参照はAのまま)
そのため、stateを更新しても参照が変わっていないため表示は変化しません。
(AとAは同じ参照のため、reactが差分がないと判断して再描画しない)
この時のstateをshallow copyされたオブジェクトと呼びます。
② 非破壊的変更を行うハンドラー
この関数では新たにオブジェクトを作成して、そのオブジェクトの値を変更しています。
(オブジェクトBを作成して、オブジェクトAの値をコピー&値を上書き)
この時のstateをdeep copyされたオブジェクトと呼びます。
そのため、stateを更新すると参照も変わっているため表示が変化します。
(AからBに参照が変わっているため再描画される)
動作サンプルを作りましので試してみると理解が促進されると思います↓
https://codesandbox.io/s/floral-snow-fp6fxr?file=/src/App.js
なお、上述した「参照が変わったら再描画される」という動きは、値を変えていない場合にも同様です。
つまり必要性がないケースで再描画されるコードを書いてしまう可能性があるという状況が想定されます。
この意図せず再描画される・されないケースを防ぐために、immutable.jsやimmerと呼ばれるライブラリが利用されることがあります。
恐らく、質問者さんの受けた指摘?は正確には「オブジェクトの参照が変わらない方法でstateを更新しているため、意図しない再描画が行われる可能性があります」ではないでしょうか?