React Hooksとは何か

# React Hooksとは？
React Hooksのおかげで、関数型コンポーネントでstateやライフサイクルを扱えるようになった。
（もともとReactでstateやライフサイクルを扱うにはクラスコンポーネントで書かなければならなかった。）

# 関数型コンポーネントの何がいいの？
## 関数型プログラミングとは
関数型プログラミングは、y=f(x)のような書き方をする。
入力が同じなら結果は同じになる。
入力値以外の関数外の変数の影響を受けない/与えない。

![スクリーンショット 2021-11-28 17.11.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2284107/05db8736-3a1a-9d1a-0432-ed02003746b7.png)


## 関数型のメリット

- 非同期処理のタイミングによって描画結果が変わらない
- 関数外のコードに影響を受けない

ちなみに関数型のデメリットは書き方に慣れが必要ってことぐらいしか見つからなかった。


# React Hooksの使い方

## useState
テキストを入力するフォームを作る
![図13.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2284107/419998ac-18a4-f61a-adc2-ff905dfc91c2.jpeg)


```jsx
import React from 'react';
import {
  View, Text, TextInput, StyleSheet,
} from 'react-native';

// eslint-disable-next-line react/function-component-definition
export default function TestScreen() {
  return (
    <View style={styles.container}>

      <View style={styles.inputContainer}>
        <Text style={styles.inputTitle}>Input</Text>
        <TextInput
          value=""
          style={styles.input}
        />

      </View>

    </View>
  );
}
```

しかし、このままではinputエリアに何も入力できない。
そこで、HooksのuseStateを使って下記のように実装する。

![図14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2284107/25847d65-e1f2-1059-f875-47106d3f61ee.png)

これで文字の状態を保持できるようになった。
![図15.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2284107/4d33a3ae-b08b-1336-6490-87b8bd913d0c.jpeg)



## useEffect

ユーザーログインを監視して、ログインしていたらメモリスト画面に遷移されるようにする。

LoginScreen.jsx

```jsx
import React, { useState, useEffect } from 'react';
import {
  View, Text, TextInput, StyleSheet, TouchableOpacity, Alert,
} from 'react-native';
import firebase from 'firebase';

import Button from '../components/Button';

// eslint-disable-next-line react/function-component-definition
export default function LoginScreen(props) {
  const { navigation } = props;
  const [email, setEmail] = useState('');
  const [pass, setPass] = useState('');

  useEffect(() => {
    firebase.auth().onAuthStateChanged((user) => {
      if (user) {
        navigation.reset({
          index: 0,
          routes: [{ name: 'MemoListScreen' }],
        });
      }
    });
  }, []);

  function handleLogin() {
    firebase.auth().signInWithEmailAndPassword(email, pass)
      .then((userCredential) => {
        const { user } = userCredential;
        console.log(user.uid);
        navigation.reset({
          index: 0,
          routes: [{ name: 'MemoListScreen' }],
        });
      })
      .catch((error) => {
        Alert.alert(error.code);
      });
  }

  return (
    <View style={styles.container}>

      <View style={styles.inputContainer}>
        <Text style={styles.inputTitle}>Login</Text>
        <TextInput
          value={email}
          style={styles.input}
          onChangeText={(text) => { setEmail(text); }}
          /* 入力の最初の文字を大文字にしない */
          autoCapitalize="none"
          /* キーボードのタイプをemailにする */
          keyboardType="email-address"
          placeholder="Email"
          /* iOSでキーチェーンと紐づけてくれる */
          textContentType="emailAddress"
        />
        <TextInput
          value={pass}
          style={styles.input}
          onChangeText={(text) => { setPass(text); }}
          placeholder="Password"
          /* 入力された文字を隠す */
          secureTextEntry
          /* iOSでキーチェーンと紐づけてくれる */
          textContentType="password"
        />

        <Button
          onPress={handleLogin}
        >
          Submit
        </Button>

        <View>
          <Text style={styles.footerText}>Not resistered?</Text>
          <TouchableOpacity
            onPress={() => {
              navigation.reset({
                index: 0,
                routes: [{ name: 'SignUpScreen' }],
              });
            }}
          >
            <Text style={styles.footerLink}>Sigh up here</Text>
          </TouchableOpacity>
        </View>

      </View>

    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#FCE4EC',
  },
  inputContainer: {
    paddingHorizontal: 24,
    paddingVertical: 24,
  },
  inputTitle: {
    fontSize: 24,
    lineHeight: 40,
    fontWeight: 'bold',
    marginBottom: 14,
  },
  input: {
    fontSize: 16,
    height: 40,
    backgroundColor: '#fff',
    borderColor: '#ddd',
    borderWidth: 1,
    paddingHorizontal: 14,
    marginBottom: 10,
  },
  footerText: {
    fontSize: 16,
    lineHeight: 32,
  },
  footerLink: {
    fontSize: 16,
    lineHeight: 32,
    color: '#D81B60',
  },
});
```
解読すると、
![図16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2284107/de6f04d2-12f3-555a-0a0d-f4f2eaedc419.png)

Reactのコンポーネントが再レンダリングされるタイミングは

- stateの変更
- propsの変更
- 親がレンダリングされた時

なので、コンポーネント内のstateでカウントアップやtrue/falseなどのフラグを扱うときに、stateを変更するたびにコンポーネントが再レンダリングされてしまう。
useEffectで非効率的な再レンダリングを制御する。
useEffectの第二引数にリストを渡すと、
リストが空 → コンポーネントが読み込まれた最初だけuseEffectの第一引数の関数が実行される
リストに変数 → 変数が変更されたとき、useEffectの第一引数の関数が実行される

## useEffectでunmountされるときの処理を記述
下記のようにuseEffectを記述した場合、
第二引数は空のリストなので、コンポーネントレンダリングされた最初だけconsole.log('Effect')が実行される。
return 処理は、コンポーネントがunmountされるときに実行される。

```jsx
useEffect(() => {
  console.log('Effect');
  return () => {
    console.log('unmount');
  };
}, []);
```

ログイン画面で、ユーザーログイン監視をログイン画面がunmountされるときにキャンセルする。
unsubscribeに関数を記述して、return unsubscribeとするだけ。

```jsx
useEffect(() => {
  const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
    if (user) {
      navigation.reset({
        index: 0,
        routes: [{ name: 'MemoListScreen' }],
      });
    }
  });
  return unsubscribe;
}, []);
```