masalibの日記

システム開発、運用と猫の写真ブログです

React hooksの入門(useState)

今どき、Reactをやっているなら絶対に避けて通れないのが「React hooks」です。他のツールと違って公式(Facebook)が作っているので今後も使われていくでしょう。 hooksの中の基本中の基本のuseStateを使ってクエリーの結果を変えたいと思います

useStateを公式で勉強

React hooksの勉強でのお決まりのプログラムは以下です

function Counter({initialCount}) {
  const [count, setCount] = useState(initialCount);
  return (
    <>
      Count: {count}
      <button onClick={() => setCount(initialCount)}>Reset</button>
      <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
      <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
    </>
  );
}

https://reactjs.org/docs/hooks-reference.html#gatsby-focus-wrapper


変数の宣言とその変数に対して変更する関数を作成している

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

setCountという名前はset + 作りたい変数(最初は大文字)にするみたいです。


変数に対して変更したい内容をセットしています

setCount(prevCount => prevCount - 1)}>

prevCountは変更する前の内容で prev + 作った変数(最初は大文字)にするみたいです。

setCount(count - 1)

公式じゃないサンプルだとsetCountに普通に値をいれていた・・・

クエリーを変更できるようにする

変更したソースは以下のとおりです

+ import React , { useState }from 'react';
- import React from 'react';
import {ApolloProvider} from '@apollo/client';
import client from './client'
//import {Issues} from './Issues';
import SearchRepositories from './SearchRepositories';

function App() {
-   let query ='graphql'
+   const intialState ='graphql'
+   const [query, setQuery] = useState(intialState)

  return (
      <>
    <div className="App">
+           <p>
+                {query}のリポジトリ一覧
+           </p>
+           <input id="query" type="text" value={query} onChange={(e) => setQuery(e.target.value)}/>
    </div>
    <ApolloProvider client={client}>

      <SearchRepositories query={query}/>

    </ApolloProvider>
    </>    
  );
}
export default App;

ソースの解説

この部分でStateに対して初期化しています。変数が1つなのでそのままいれる事も可能だったのですが 配列でいれるパターンも考慮して一度、変数にいれてからuseStateにしています

const intialState ='graphql'
const [query, setQuery] = useState(intialState)

変更した文字(e.target.value)をsetQueryという関数を使用して変更しています

<input id="query" type="text" value={query} onChange={(e) => setQuery(e.target.value)}/>

(e) => setQuery(e.target.value)は入力された文字(1文字でも)を渡すお決まりのパターンです。 欠点としては1文字単位でAPIを叩いているのであまり良くないプログラムです

f:id:masalib:20201108000957g:plain

クエリーを変更できるようにする2

クエリーを投げる前と実際になげるクエリーを1つのステート(state)にもったパターンです。

import React , { useState }from 'react';
import {ApolloProvider} from '@apollo/client';
import client from './client'
//import {Issues} from './Issues';
import SearchRepositories from './SearchRepositories';


function App() {
+  const intialState = {
+    beforeQuery:'graphq',
+    procQuery:'graphql'
+  }

+  const [state, setState] = useState(intialState)

+  const changeQuery = e => {
+    e.preventDefault()
+    console.log(state.beforeQuery)
+    console.log(state.procQuery)
+    setState({...state,procQuery:state.beforeQuery})
+  }

  return (
      <>
    <div className="App">
          <p>
+                {state.beforeQuery}のリポジトリ一覧
          </p>
+          <input id="query" type="text" value={state.beforeQuery} onChange={(e) => setState({...state,beforeQuery:e.target.value})}/>
+          <button onClick={changeQuery}  >
+            検索
+          </button>
    </div>
    <ApolloProvider client={client}>

      <SearchRepositories query={state.procQuery}/>

    </ApolloProvider>
    </>    
  );
}
export default App;

ソースの解説1

Stateに対して初期化しています。

  const intialState = {
    beforeQuery:'graphq',
    procQuery:'graphql'
  }

  const [state, setState] = useState(intialState)

配列にする事で1つのstateで複数の状態がもてます。普通に変数を分けてもいいのですが状態の数が多くなる事を考慮するとこの形がいいかなと

ソースの解説2

Stateに対して変更処理をしている部分がポイントです

<p>
    {state.beforeQuery}のリポジトリ一覧
</p>
<input id="query" type="text" value={state.beforeQuery} onChange={(e) => setState({...state,beforeQuery:e.target.value})}/>

setState({...state,beforeQuery:e.target.value})

setStateはオブジェクトで渡すが可能です。状態が複数ある場合は前回の状態と変更する変数またはオブジェクトを渡す必要があります

公式だと

setState(prevState => {
  // Object.assign would also work
  return {...prevState, ...updatedValues};
});

もう1つのオプションはuseReducerです。これは、複数のサブ値を含む状態オブジェクトの管理に適しています。 今回はuseReducerを使いません。いずれ勉強したい。

ソースの解説3

const changeQuery = e => {
  e.preventDefault()
  console.log(state.beforeQuery)
  console.log(state.procQuery)
  setState({...state,procQuery:state.beforeQuery})
}

<button onClick={changeQuery}  >
検索
</button>

検索ボタンが押すとbeforeQueryをprocQueryに書き換える事でAPIを叩く事にしています

結果

f:id:masalib:20201108002748g:plain

1文字単位の変更でAPIが叩かれる事はなくなりました。 ただ1文字が入力される度に「SearchRepositories」のコンポーネントが読み込まれているみたい・・・ キャッシュがあるからAPIに再取得はしていないっぽい。あまり良くないのですがAPIを叩いていないのでいいかなと思う

参考URL

https://qiita.com/seira/items/f063e262b1d57d7e78b4 https://qiita.com/10mi8o/items/78e43c16610972f6c0c6