0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

React.js: 状態(state)の構造を選択する

Posted at

image.png

はじめに

Reactの公式ドキュメントで参考になったページがあったので、自分の学習用にまとめました。
参考にしたページはこちら


Reactコンポーネントの状態(state)の構造を適切に設計することは、保守性に大きく影響を与えます。

状態を構築する際に考慮すべき5原則は以下です。

state構造設計のための5原則

  1. 関連する状態のグループ化
  2. 状態の矛盾を避ける
  3. 冗長な状態を避ける
  4. 状態内の重複を避ける
  5. 深いネストの状態を避ける

5原則それぞれ詳細について以下に記載します。

関連する状態のグループ化

例えば、コンピューターゲームのキャラクターを考えてみましょう。このキャラクターはx 座標とy 座標で移動できます。これら x と y の値を状態として管理するとしたら、どのように記述しますか?

悪い例

const [x, setX] = useState(0);
const [y, setY] = useState(0);

良い例

const [position, setPosition] = useState({ x: 0, y: 0 });

技術的には、どちらのアプローチも使用可能ですが、2つ以上の状態が常に同時に変化する場合は、1つの状態変数にまとめるのが良いでしょう。これにより、同期を保つことを忘れずに済みます。

状態の矛盾を避ける

例えば、メッセージングアプリを考えてみましょう。メッセージ送信の承認には、2つの異なるステージがあります。1つ目は「メッセージ送信中( isSending )」、2つ目は「メッセージ送信済み( isSent )」です。これらの2つの状態を、それぞれtrueやfalseのブール値として別々の状態変数で宣言すると、どのような問題が発生するでしょうか?

悪い例(状態の矛盾が起こる可能性がある)

const [isSending, setIsSending] = useState(false);
const [isSent, setIsSent] = useState(false);

良い例

const [status, setStatus] = useState('typing'); 
// 'typing', 'sending', 'sent' のいずれか

この方法により、状態の矛盾を防ぎ、コードの可読性と保守性を向上させることができます。

冗長な状態を避ける

例えば、firstNameとlastNameからfullNameを計算できる場合、fullNameを状態として保持するのは冗長です。必要に応じてレンダー時に計算することで、状態の冗長性を避けられます。

悪い例

const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [fullName, setFullName] = useState('');

良い例

const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const fullName = firstName + ' ' + lastName;

このように、計算可能な情報は状態として保持せず、必要に応じて計算することで、状態管理をシンプルに保つことができます。

状態内の重複を避ける

同じデータが複数の場所で管理されていると、更新時に不整合が生じる可能性があります。

悪い例(状態の重複あり)
以下のコードでは、items 配列と selectedItem の両方で同じデータが管理されており、重複しています。

const initialItems = [
  { title: 'pretzels', id: 0 },
  { title: 'crispy seaweed', id: 1 },
  { title: 'granola bar', id: 2 },
];

export default function Menu() {
  const [items, setItems] = useState(initialItems);
  const [selectedItem, setSelectedItem] = useState(
    items[0]
  ); // 重複

setItems(items.map(item =>
  item.id === id ? { ...item, title: e.target.value } : item
));

良い例(重複を排除)

const initialItems = [
  { title: 'pretzels', id: 0 },
  { title: 'crispy seaweed', id: 1 },
  { title: 'granola bar', id: 2 },
];

export default function Menu() {
  const [items, setItems] = useState(initialItems);
  const [selectedId, setSelectedId] = useState(0);

  const selectedItem = items.find(item =>
    item.id === selectedId
  );

このように、関連する情報を一つのオブジェクトにまとめて管理することで、データの重複や不整合を防ぐことができます。

深いネストの状態を避ける

状態が深くネストされていると、更新が複雑になり、バグの原因となります。可能な限り、状態はフラットな構造にし、必要に応じて複数の状態変数に分割することを検討してください。

悪い例

// 深くネストされた状態
const [state, setState] = useState({
  user: {
    name: '',
    age: 0,
    address: {
      city: '',
      zip: ''
    }
  }
});

// 更新が複雑になる例
setState(prevState => ({
  ...prevState,
  user: {
    ...prevState.user,
    address: {
      ...prevState.user.address,
      city: 'Tokyo'
    }
  }
}));

良い例

// フラット化した状態管理の例
const [userName, setUserName] = useState('');
const [userAge, setUserAge] = useState(0);
const [userCity, setUserCity] = useState('');
const [userZip, setUserZip] = useState('');

この方法では、各値を直接更新できるため、保守性と可読性が向上します。

まとめ

Reactの状態管理の良い設計として、以下の点を押さえておきましょう

  1. 関連する状態のグループ化:複数の変数が同時に変更される場合はまとめる
  2. 状態の矛盾を避ける:矛盾しないよう、状態を一つの変数で管理する
  3. 冗長な状態を避ける:計算可能な値は状態として保持せず、計算する
  4. 状態内の重複を避ける:重複データを排除し、一元管理する
  5. 深いネストを避ける:状態のネストは浅く保ち、フラットに管理する

これらの原則を意識することで、コードの可読性と保守性が向上し、バグの少ないReactアプリケーションの構築に近づくことができると思います。

ぜひ参考にしてみてください!

0
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?