【React】useStateについて分かりやすく解説

本記事はこんな方に向けて書いています。

  • useStateがいまいち分からない人
  • 関数コンポーネントでuseStateを使いたい人
  • 手を動かしながら、useStateを使いたい人

こんにちは!けいです。

Reactには状態管理を行う『useState』という便利な機能があります。この機能を学ぼうと思っても、昔のクラスコンポーネントを使って説明されたり、難しすぎる解説をしている記事が多い印象です。

本記事では、Reactを触り始めた初学者の方でも、useStateを理解できるように簡単な内容にしたのでぜひ参考にしてください!

Reactを使う準備をしよう

何はともあれ、Reactを使う準備をしていきます。Reactでアプリを作るにあたって、必要なフォルダを1から作成する必要はありません。

「create-react-app」という便利な機能を使うと、Reactアプリの雛形を作成してくれます。

ターミナルで下記コマンドを実行します。

npx create-react-app usecontext_practice

//コメント
↑usecontext_practiceの部分は好きな名前でOKです。

上の処理が終わったら、ターミナルで「yarn start」を実行します。
そうするとブラウザが自動で立ち上がり、「React」ロゴがグルグル回る画面が開きます。

準備が整ったので、次に進みましょう。

ボタンでuseStateを使ってみよう

create-react-appでアプリを作成すると、App.jsには様々な記載がされていますが、今回は不要なので下記コードを貼り付けてください。

import React from 'react'

function App() {
  return (
    <div>
      
    </div>
  )
}

export default App

Appの部分は『アロー関数』で書かれる場合もありますが、どちらでもOKです。

import React from 'react'

const App() => {
  return (
    <div>
      
    </div>
  )
}

export default App

次はApp.jsの<div>の中身にボタンの要素を追加してみましょう。

import React from 'react'

function App() {
  return (
    <div>
      <h1>useStateの練習</h1>
      <h2>カウント:ここにカウントの値が入ります。</h2>
      <button>-1</button>
      <button>+1</button>
    </div>
  )
}

export default App

ここまででブラウザは、下画像のようになります。
ボタンを押すと、「ここにカウントの値が入ります。」の部分が動的に変わるようにするのが目標です。

いよいよuseStateを使っていきます。
App.jsの内容を下記コードのように変更してください。

import React, { useState } from "react";

function App() {
  const [count, setCount] = useState(0);
  return (
    <div className="App">
      <h1>useStateの練習</h1>
      <h2>カウント:{count}</h2>
      <button>-1</button>
      <button>+1</button>
    </div>
  );
}

export default App;

コードを貼り付けたら、ブラウザが下画像のようになっていることを確認してください。この時点では、ボタンを押してもカウントの値は変わりません。

ボタンを実装する前に、useStateについて解説していきます。

まずReactでuseStateを使用する場合は、このようにインポートする必要があります。

import React, { useState } from "react";

今回は「カウントという値」という状態を管理したいので、下記のように書きます。countが「現在のカウントの値(= state)」、setCountは「カウントを変更する関数」を表します。

stateを変更する関数の名前は「set”state名”」としてください。またuseStateの引数は、stateの初期値を設定できます。countの初期値は、0になっています。

const [count, setCount] = useState(0);

次にボタンを押すと、カウント値が増減するようにします。
App.jsのコードを下記のように書き換えてください。

import React, { useState } from "react";

function App() {
  const [count, setCount] = useState(0);
  return (
    <div className="App">
      <h1>useStateの練習</h1>
      <h2>カウント:{count}</h2>
      <button onClick={() => setCount(count - 1)}>-1</button>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
}

export default App;

これで、ボタンを押すとカウント値を増減できるようになりましたね!

setCountの引数に、現在のカウント(count)から1を引いたものを渡すことで、カウント値を減らすことができます。増やす場合も同様の考え方です。

入力フォームでuseStateを使ってみよう

次にメニューを入力して、注文数を増減できる簡単なアプリを作成してみましょう。App.jsを開き下記コードを貼り付けてください。

import React, { useState } from "react";

function App() {
  const [order, setOrder] = useState();
  const [count, setCount] = useState();
  return (
    <div className="App">
      <h1>useStateの練習</h1>
      <br />
      <div>
        <label htmlFor="order">注文内容:</label>
        <input
          id="order"
          value={order}
          onChange={(e) => setOrder(e.target.value)}
        />
      </div>
      <br />
      <button onClick={() => setCount(count - 1)}>-1</button>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <button onClick={() => setCount(0)}>リセット</button>
      <h2>
        {order}を{count}個注文します。
      </h2>
    </div>
  );
}

export default App;

「onChange」というイベントハンドラを使うと「e」が返ってきます。eにはイベントの情報が含まれています。(ここではあまり深く考えないでOKです!)

入力フォームの値は、「e.target.value」で取得可能です。これは公式のようなものなので覚えちゃってください!

この時点で、ブラウザでは下画像のように表示されています。

では、入力フォームに、好きな文字を入力&ボタンをクリックして、挙動を確認してみてください。このようになれば完成です!

最後にコードをもう少しみやすく(リファクタリング)していきます。
App.jsのコードに下記を貼り付けてください。

App.defaultPropsに、stateの初期値を定義しています。この初期値が「props」として渡ってくるので、Appの中で使用できるように引数にpropsを渡します。

import React, { useState } from "react";

function App(props) {
  const [order, setOrder] = useState(props.order);
  const [count, setCount] = useState(props.count);
  return (
    <div className="App">
      <h1>useStateの練習</h1>
      <br />
      <div>
        <label htmlFor="order">注文内容:</label>
        <input
          id="order"
          value={order}
          onChange={(e) => setOrder(e.target.value)}
        />
      </div>
      <br />
      <button onClick={() => setCount(count - 1)}>-1</button>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <button onClick={() => setCount(props.count)}>リセット</button>
      <h2>
        {order}を{count}個注文します。
      </h2>
    </div>
  );
}

App.defaultProps = {
  order: "",
  count: 0,
};

export default App;

propsは下のようにオブジェクトの形になっています。
orderの値は「props.order」、countは「props.count」で取り出せます。

props = {order: '', count: 0} 

リファクタリングしてコードをみやすくしよう

上の章でリファクタリングを行いましたが、もう少しコードを見やすくすることができます。

まずはApp.jsに下記コードを貼り付けてください。

import React, { useState } from "react";

function App(props) {
  const [state, setState] = useState(props);
  const { order, count } = state;
  return (
    <div className="App">
      <h1>useStateの練習</h1>
      <br />
      <div>
        <label htmlFor="order">注文内容:</label>
        <input
          id="order"
          value={order}
          onChange={(e) => setState(e.target.value)}
        />
      </div>
      <br />
      <button onClick={() => setState({ ...state, count: count - 1 })}>
        -1
      </button>
      <button onClick={() => setState({ ...state, count: count + 1 })}>
        +1
      </button>
      <button onClick={() => setState(props)}>リセット</button>
      <h2>
        {order}を{count}個注文します。
      </h2>
    </div>
  );
}

App.defaultProps = {
  order: "",
  count: 0,
};

export default App;

useStateのリファクタリングについて解説

まずstateの部分を少しシンプルにしてみました。

//変更前
const [order, setOrder] = useState(props.order);
const [count, setCount] = useState(props.count);

//変更後
const [state, setState] = useState(props);

propsの中身は初期値の{order: ”, count: 0}が入っており、これがstateに渡されます。確認したい方は、上記コードの下に「console.log(state)」で、中身を確認してみてください。

ボタンのリファクタリングについて解説

次にonClickの返り値についてみてみましょう。

<button onClick={() => setState({ ...state, count: count - 1 })}>
-1
</button>
<button onClick={() => setState({ ...state, count: count + 1 })}>
+1
</button>
<button onClick={() => setState(props)}>リセット</button>

setOrderやsetCountは全てsetStateに置き換わります。
setStateの引数は、オブジェクトが渡させるので上記のように、{count: count-1}の形にする必要があります。

またコードの中にある下の表記をみて、「なにこれ??」と思った方もいるかと思います。これは「スプレッド構文」と呼ばれており、オブジェクトや配列の中から要素を取り出す便利なものです。

...state

setStateの中身を変えるとどうなるのかみてみましょう!
スプレッド構文に関する参考記事

//例えばstateがこんな状態の場合...
{state} = {order:'サンプル', count: 0}

◾︎{...state}の場合
//stateの中身がコピーされている
setState{...state} 
=> setState({order: 'サンプル', count: 0})

◾︎{count: count+1}
//orderの中身がリセットされている!
setState{(cout: count + 1)} 
=> setState({order: '', count: 1})

◾︎{...state, count: count + 1}
//orderの中身はそのままで、countだけ+1されている
setState({...state, count: count + 1}) 
=> setState{(order: 'サンプル', count: 1)}
 

これでリファクタリングに関する解説は以上です。お疲れ様でした!