0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

useState を書きながら学ぶ を書きながら学ぶ(React Hooks 入門シリーズ 4/6)

Last updated at Posted at 2021-02-04

ロードマップ

React 16.8 で追加された機能であるReactのHooksについて書いてあります。

書きながら学ぶ React Hooks 入門シリーズとして書き下ろしました。

はじめに

Reactの組み込みフックであるuseStateの説明をします。

useState とは

state と stateの更新関数を返すフックです。

このフックを利用すれば、コンポネント内でstate管理ができます。

state が 更新されるとコンポネントは再レンダリングされます。

構文

const [state, setState] = useState(state初期値)

活用例...カウントアプリ

サンプルコード
App.js
import React, { useState } from "react";

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

  const increment = () => setCount(count + 1);

  return (
    <div className="App">
      <p>{count}</p>
      <button onClick={increment}>+1</button>
    </div>
  );
};

export default App;

活用例...カウントアプリ(setStateの更新関数の引数に関数を渡すバージョン)

サンプルコード
App.js
import React, { useState } from "react";

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

  const increment = () => setCount((currentCount) => currentCount + 1);

  return (
    <div className="App">
      <p>{count}</p>
      <button onClick={increment}>+1</button>
    </div>
  );
};

export default App;

これの何が嬉しいのか? => ロジックをコンポネントの外に切り出せる

これによって、ロジックが外部に依存しにくくなります。また、共通化しやすくなるメリットもあります。

サンプルコード(setStateの引数に渡した関数を外部化)
App.js
import React, { useState } from "react";

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

  const increment = () => setCount((currentCount) => currentCount + 1);

  return (
    <div className="App">
      <p>{count}</p>
      <button onClick={increment}>+1</button>
    </div>
  );
};

export default App;

最初に書いたsetStateの引数だと、setState(count + 1)だと、外部に切り出せませんが

setStateの更新関数の引数に関数を渡すバージョンだと、コンポネント外に切り出せます。

App.js
import React, { useState } from "react";

// setState内のロジックを外部化
const updateCount = (currentCount) => currentCount + 1;

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

  const increment = () => setCount(updateCount);

  return (
    <div className="App">
      <p>{count}</p>
      <button onClick={increment}>+1</button>
    </div>
  );
};

export default App;

活用例...オブジェクトをどう扱うか

本の情報を扱う object を state にして表示してみましょう。

サンプルコード
App.js
import React, { useState } from "react";

const bookInfo = {
  title: "サピエンス全史",
  author: "ユヴァル・ノア・ハラリ",
  price: 2000
};

const App = () => {
  const [book, setBook] = useState(bookInfo);

  // 値が更新される関数
  const updateBookInfo = () => setBook({ ...book, price: book.price + 1 });

  // 値が更新されない関数
  const updateBookInfo2 = () => {
    book.price = book.price = 1;
    setBook(book);
  };

  return (
    <div className="App">
      <p>{book.title}</p>
      <p>{book.author}</p>
      <p>{book.price}</p>
      <button onClick={updateBookInfo}>update</button>
    </div>
  );
};

export default App;

仮に、...book, を消してしまい、関数updateBookInfoが以下のようだと...

const updateBookInfo = () => setBook({ price: book.price + 1 });

setState 更新後の値は、{price: 2001} と、priceだけになるでしょう。これは、setStateが差分だけマージじゃなく置換だからです。

注意点

buttonクリック時の関数を

  • 関数updateBookInfoにすると、値は更新される
  • 関数updateBookInfo2にすると、値は更新されない

原因は updateBookInfo2 に渡された book が変更前と同じとみなされたからです。

React の state は Object.is() で変更の有無を判定しているらしいです。

  • Object.is() が true なら 同じ(変更してない)と見なして Reactの場合コンポネントが再レンダリングされない
  • Object.is() が false なら 変更したと見なして Reactの場合コンポネントが再レンダリングされる
  • だけど、stateの中身は更新されます。コンポネントが再レンダリングされないからユーザーの目には変わってないように見えます

aea9d1cb006f9392d73740585cfdd435.gif

Object.isに関しては、以下が参考になりました。

一方で、関数updateBookInfoは、スプレッド演算子を利用して新たなオブジェクトを生成しているのでObject.is()の結果はfalseになりコンポネントが再レンダリングされます。

これは、setState関数が配列の場合でも同じです。

今回は以上です。

参考

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?