見出し画像

【React】useState完全に理解した

「完全に理解した」というミームは、実際ほとんど理解していないにもかかわらず、よく分かったような気持ちになっている状態を表すものだと認識しています。

さて、この度、ReactのuseStateを完全に理解したのでアウトプットメモを残していきます。
(「完全に理解した」レベルということで、理解度は高めに見積もって3割だと思います。間違いあればご指摘ください)

変数の取り扱いルール

useStateは、Reactのコンポーネントの中で使われる変数を管理するためのフックです。

たとえば、Rubyとかであれば以下のように変数を扱えます。

def greeting(name)
  puts "Hello, #{name}!"
  name = "john"
  puts "Hello, #{name}!"
end

puts "名前を入力してください"
name = gets.chomp
greeting(name)

結果は以下。入力を求められるので”taro”を入れてます。

ジョンがハードコードされてる意味はわからんけど

一応解説しておくと、

  1. ユーザーが名前を入力し変数nameに代入

  2. greeting(name) で、メソッドに引数nameを渡す

  3. ユーザーが入力した名前に対して Hello, "〇〇"! と表示する

  4. そのあと、なぜか必ず "Hello, john!" と表示する

コード自体は意味不明ですが、Rubyの世界では一応、ルール違反ではありません。

ところが、Reactのコンポーネントの世界では、これは禁止です。

つまり、一つの変数にいろんな値を次々代入してはいけないということです。

変わっていく状態値を管理する

Reactでは数値が変わっていくことを管理するときに、stateという「状態」を表すことで、複数の状態を移り変わるプログラムを書くことができます。

基本的な書き方は以下。

const [index, setIndex] = useState(0);

で、個人的にこれを理解するのに1週間くらいかかりました。
「分割代入です」って解説がよくあるんですが、わからんのはそこじゃない。みたいな。
で、ようやく分かったので本記事を書くに至ったわけです。

さて、登場人物(?)を順に解説すると以下のようになります。

  • const ・・・JavaScriptの通常の文法。変数を宣言するときに使う。constで宣言すると動かなくなるので、定数と呼ぶこともあるとか。

  • index ・・・ コンポーネント内で使う変数。いろんな値がセットされる。ただし普通の代入 (index = 1 とか)は受け付けない。

  • setIndex・・・変数indexに値をセットする専用の関数。セッタ関数という。
    たとえるなら、銃に弾を込めるためのマガジンみたいなイメージ。銃に対応したマガジンじゃないと弾は入らない、的な。

  • useState(0)・・・左辺の2つが「銃」と「マガジン」の関係であることを示すフック。(0)というのは、「indexの初期値は0」という意味。

つかいかた

useStateを使って宣言したindexとsetIndexの使い方は以下。

setIndex(10)
// index = 10 と同じ効果

setIndex(num) の "num"の部分に任意の値を入れることで、indexにはその値がセットされます。

ここでセットされたindexは、そのコンポーネントがレンダーされている間保持されます。(画面を更新するまで、数値が保存されるイメージ)

この特性を使えば、だんだん増えていくこともできます。

setIndex(index + 1)
// indexが1増える

自己代入を使える場合は、index = index + 1 でよさそうな挙動ですが、最初に書いた通りReactではそれは禁止なので、setIndexを使ってindexを増やします。

なんで自己代入ではダメなのか?

さて、rubyでは index = index + 1  でよかったものを、ReactではsetStateなどという不思議な書き方で書かないといけないのでしょうか?

いろいろ調べていると、どうやらスコープの問題があるみたいです。

スコープとは、その変数がアプリ全体のどこで使えるかといった「範囲」のことです。
Rubyでは、メソッド内でどれだけ変数"name"を書き換えても、その「変数name」自体が他のメソッドに影響を与えることはありません。

def greeting(name)
  puts "Hello, #{name}!"
  name = "john"
  puts "Hello, #{name}!"
end

puts "名前を入力してください"
name = gets.chomp
greeting(name)

最初に見せたこのRubyのコードでは、変数nameにはユーザー入力の(getsした)値が残っています。メソッドの中でjohnに書き換えられていても、外界には影響がないのです。

しかし、JavaScriptを使うReactでは、関数の外で宣言された変数はグローバル変数となってしまい、アプリ全体から使用できてしまいます。
これでは、「今nameに何という値が入っているか」を管理するのは至難の業です。

そこで出てくるのがStateです。

const [index, setIndex] = useState(0);

ここで定義したindexは、そのstateが呼び出されるコンポーネントの中だけで有効になります。
つまり、ここでindexを100にしようが1000にしようが、アプリの他の部分に
は影響を及ぼしません。

このスコープが広いと何が困るかって、
たとえばAmazonで買い物するときに、商品Aと商品Bをカゴに入れたとします。
そのあと商品Bの数量を3つに変更したら、商品Aも3つに増えてしまった。
みたいなことが起きるわけです。
これは変数「itemCount」みたいなものが共通で使われていて、商品数=3が代入されてしまったということですね。
当然、こんなアプリは使い物にならないので、こんなもんを作らないようにルールがあるわけです。

以上のように、「管理できる範囲だけで通じる変数」を規定するために、useStateを使って変数を管理する必要があるというわけです。
そのために、単なる代入(index = 10とか)ではなく、setIndex(10)を使う必要があるということです。

細かいルール

useStateは、コンポーネントのトップレベルで宣言する必要があります。要は、コンポーネントの記述が始まったら一番に宣言しておけばOKです。
if文の中とかにネストするのも当然NGです。

export default function Gallery() {
  const [index, setIndex] = useState(0);

あと、useStateが複数でてくることもあります。

  const [index, setIndex] = useState(0);
  const [showMore, setShowMore] = useState(false);

このように複数のuseStateが登場することもよくありますが、これらはなんと「出てきた順番に使う」というルールで、ごちゃ混ぜにならないようになっています。
これがごちゃ混ぜになってしまうようなら、リファクタリングしてコンポーネントを分割したほうが良いんでしょうね。きっと。

まとめ

ReactでuseStateをつかう理由は大きく以下。

  • スコープを限定して他のコンポーネントに影響させないため

  • 今の状態を管理して、次の読み込みまで値を保持しておくため

使い方は以下。

  • コンポーネントの初めに、
    const [something, setSomething] = useState(初期値)
    で変数とセッタ関数、初期値を宣言する。

  • この宣言は条件分岐やネストされたコードの中に入れないで、必ずトップレベルで宣言する。

  • 使う時は、宣言した順番に使う。


ということで、プログラミング学習4ヶ月目のサラリーマンがReactのuseState完全に理解した、という話でした。
最後までお読みいただきありがとうございました。


この記事が気に入ったらサポートをしてみませんか?