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.

React Hooks(導入、Props、useState、useReducer、useEffect、axios)

Last updated at Posted at 2021-06-30

はじめに

React Hooksの基礎について学んだことを2回に分けて記事にしました。

今回はReact Hooks(導入、Props、useState、useReducer、useEffect、axios)
次回、React Hooks(useContext、useMemo、useCallback)

開発環境

React: 17.0.2
Node.js: 14.16.0
Visual Studio Code: 1.57.1

Function Component と Class Component

本稿では先にHooksについての説明をすることを前提に進めていくので、Function Componentでの説明をします。その前提知識としてFunction Component と Class Componentついて少しだけお話しします。

フック(hook)が React 16.8 で追加される前までは、component間でのデータの扱いを柔軟に行うためにはClassComponentで書く方法しかありませんでした。ClassComponentはJavaScript特有のクラスの概念の理解が必要な上に、状態管理の一元管理ができずライフサククル別の処理が必要になり、そういったことがエラーの原因になっていました。フック(hook)の導入でFunctionComponentによる多様なcomponent間での状態の共有を可能にするなど様々な機能により、見通しが良く、少ない作業での実装が可能になりました。

creat-react-app

まずファイルを生成

create-react-app example

フォルダー構造

.
├── node_modules
├── README.md
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
├── src
│   ├── App.css
│   ├── App.test.js
│   ├── App.js
│   ├── index.css
│   ├── index.js
│   ├── logo.svg
│   ├── reportWebVitals.js
│   └── setupTests.ts
├── tsconfig.json
└── yarn.lock

public/index.html が一番上の階層のhtmlファイル。

public/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <!--  -->
    <div id="root"></div>
    <!--  -->
  </body>
</html>

下記のReactDOM.render()からid="root"と紐づいて、上記の<div id="root"></div>App.jsが出力されている。

src/index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root")
);

reportWebVitals();

component生成

src直下にcomponentsというフォルダを作成してその中にBasic.jsというファイルを作成。

ちなみに、VSCodeの拡張機能ES7 React/Redux/GraphQL/React-Native snippets をインストールすると、componentの中でrafceと入力するだけで、補完機能を使用してfunctional componentの雛形を作成できる。

src/components/basic.js
import React from 'react'

const Basic = () => {
    return (
        <div> // 必ずdivタグやReact.Fragmentなどで囲わないといけない
            
        </div>
    )
}

export default Basic

React.Fragmentの省略

React.fragmentはDOMに余分なノードを追加することなく子要素をまとめることができるようになります。
またReact.fragmentには省略記法があります。

 <React.Fragment></React.Fragment> 
// 以下のように省略できます
<></> 

Basic.jsを表示させる

Basic.jsを以下のように書き換えて、App.jsに渡して表示させる。

Basic.js
import React from "react";

const Basic = () => {
  return (
    <>
      <h1>Hello World!</h1> 
    </> 
  );
};

export default Basic;

App.jsの中身は初期状態からいらない要素を削除してBasic.jsのインポートとBasicタグを追加。

App.js
import "./App.css";
import Basic from "./components/Basic";

function App() {
  return (
    <div className="App">
      <Basic />
    </div> 
  );
}

export default App;

Props

Basic.jsの表示内容をApp.jsから変更する。
App.jsの<Basic />に変数と値を指定する。

App.js
import "./App.css";
import Basic from "./components/Basic";

function App() {
  return (
    <div className="App">
      {/* titleという変数を持たせて"React!!"を代入 */}
      <Basic title="React!!" />
    </div>
  );
}

export default App;

App.jsで指定した、titleをBasic.jsで受け取る。
propsというオブジェクトを受け取るとその中にtitleが入っている。

Basic.js
import React from "react";

const Basic = (props) => {  // propsを受け取る
  return (
    <>
      {/* propsから中身を取り出す */}
      <h1>Hello {props.title}</h1>
    </>
  );
};

export default Basic;

clickイベント

クリックしたらコンソールログを表示させるという簡単な関数を作成し、buttonタグに指定する。

Basic.js
import React from "react";

const Basic = (props) => {
  const log = () => { // logという関数を作成
    console.log("クリック!");
  };
  return (
    <>
      <h1>Hello {props.title}</h1> 
    {/* onClickに作成したlog関数を指定 */} 
      <button onClick={log}>クリック</button> 
    </> 
  );
};

export default Basic;

useStateを使ってクリックするとブラウザーで表示されているカウントが1ずつカウントする関数を作ります。
ブラウザーでクリックボタンをクリックするとコンソールにクリック!と表示されることができる。

useState

useStateは定義した変数の状態の保持し、変数の更新するための関数を返します。
下記はコードはボタンを押すたびにカウントが増えていくという簡単な例。

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

const Basic = () => {
  // countという変数を定義し、useState(0)でcountの初期値に0を設定
  // setCountはcountを更新するための関数名です。
  // ちなみにこのuseState左辺の第二引数の関数名は(set変数名)とするのが慣習のようです。
  const [count, setCount] = useState(0);

  return (
    <>
      <button
        onClick={() => {
            // countをaとして受け取り、+1して返している
          setCount((a) => a + 1);
        }}
      >
        カウント {count}
      </button> 
    </> 
  );
};

export default Basic;

これでボタンを押すごとにカウントされる。

useStateでオブジェクト扱う

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

const Basic = () => {
  const [User, setUser] = useState({ name: "", age: "" });

  return (
    <>
      <form>
        <input
          type="text"
          value={User.name}
        // onChangeは一文字入力するごとに値が更新され、ブラウザーに動的に表示される。
        // ...Userはオブジェクトを分解して、nameの値の更新と、残りのUser要素もそのまま返す必要があるため。
          onChange={(user) => setUser({ ...User, name: user.target.value })}
        />
        <input
          type="text"
          value={User.price}
          onChange={(user) => setUser({ ...User, age: user.target.value })}
        />
      </form>
      <h1>名前 {User.name}</h1> 
      <h1>年齢 {User.age}</h1> 
    </>
  );
};

export default Basic;

useStateで配列を扱う

オブジェクトとほとんど扱い方は同じ。

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

const Basic = () => {
  const [users, setUsers] = useState([]);
  const newUsers = () => {
    setUsers([
      ...users,
      {
        id: users.length,
        name: "name",
      },
    ]);
  };

  return (
    <>
      <button onClick={newUsers}>add</button>
      <ul>
          {/* users配列から一つずつuserを取り出して加工 */}
        {users.map((user) => (
          <li key={user.id}>
            {user.name} id: {user.id}
          </li>
        ))}
      </ul>
    </>
  );
};

export default Basic;

useReducer

useReducerはuseStateにはないreducerという機能を使用して、より複雑な処理をしたり、コンポーネントをまたいでの状態の更新などができます。

// reducerはstateを更新するための関数で、dispatchは、reducerを実行するための呼び出し関数です
// initialArgはstateの初期値、initは初期化関数を持たせることができる。
const [state, dispatch] = useReducer(reducer, initialArg, init);

次の例でまず、buttonタグ内でtypeを持ったdispatchが設定され、dispatchが起動するとreducerが呼び出され、reducerがactionに格納されたtypeによって条件毎に異なるstateを返すことがわかります。

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}.    //dispatch()の引数がactionに入る
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

useEffect

useEffect に渡された関数はレンダーの結果が画面に反映された後に動作します。

デフォルトでは渡された関数はレンダーが終了した後に毎回動作しますが、特定の値が変化した時のみ動作させるようにすることもできます。

// レンダー終了後毎回走る
useEffect(() => {
  console.log('Hi!')
})
// 最初のレンダー終了後だけ走る
useEffect(() => {
  console.log("Hi");
}, []);
// fooに変更がある度に走る
useEffect(() => {
  console.log("Hi");
}, [foo]);

useEffectへ渡す関数がsetIntervalのような永続的な場合はメモリーリークを無くすため、下記のようにコンポーネントが消えるタイミングで関数を終了させる。

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

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

  const time = () => {
    setCount((value) => value + 1);
  };

  useEffect(() => {
    const interval = setInterval(time, 1000);
    return () => {
      clearInterval(interval); // component破棄時に走る
    };
  }, []);
  return <>{count}</>;
}

export default Basic;

axiosとFetchAPI

JSONPlaceholderというテスト開発用に無料でデータを提供しているAPIサービスを使用して、axiosを使った方法とJavaScriptメソッドのFetchAPIの2通りのAPIの取得方法です。

axios

import React, { useState, useEffect } from "react";
import axios from "axios";

const Basic = () => {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    axios.get("https://jsonplaceholder.typicode.com/Users").then((res) => {
      setUsers(res.data);
    });
  }, []);

  return (
    <>
      <ul>
        {users.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </>
  );
};

export default Basic;
import React, { useState, useEffect } from "react";

const Basic = () => {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    fetch(`https://jsonplaceholder.typicode.com/Users`)
      .then((res) => res.json())
      .then((data) => {
        setUsers(data);
      });
  }, []);

  return (
    <>
      <ul>
        {users.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </>
  );
};

export default Basic;

asios

JSONPlaceholderというテスト開発用に無料でデータを提供しているAPIサービスから、axiosを使ったAPIデータを取得します。

axiosで受け取ったデータをUsersにsetして、そのUsersをmapで一覧表示します。

import React, { useState, useEffect } from "react";
import axios from "axios";

const Basic = () => {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    axios.get("https://jsonplaceholder.typicode.com/Users").then((res) => {
      setUsers(res.data);
    });
  }, []);

  return (
    <>
      <ul>
        {users.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </>
  );
};

export default Basic;

次はJavaScriptメソッドのFetchAPIを使用した方法です。
axiosとは違い、HTMLの形式でデータが取得されるのでjsonメソッドで変換しています。

import React, { useState, useEffect } from "react";

const Basic = () => {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    fetch(`https://jsonplaceholder.typicode.com/Users`)
      .then((res) => res.json())
      .then((data) => {
        setUsers(data);
      });
  }, []);

  return (
    <>
      <ul>
        {users.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </>
  );
};

export default Basic;

次回、React Hooks(useContext、useMemo、useCallback)

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?