入力フォームの実装

# メモを編集する画面を実装

## MemoEditScreenを作成
src/screensの中にMemoEditScreen.jsxを作成する。
MemoEditScreen.jsx

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

import AppBar from '../components/AppBar';
import CircleButton from '../components/CircleButton';

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

      <View style={styles.inputContainer}>
        <TextInput value="買い物" multiline style={styles.input} />
      </View>

      <CircleButton name="check" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  inputContainer: {
    paddingVertical: 30,
    paddingHorizontal: 24,
    flex: 1,
  },
  input: {
    fontSize: 20,
    lineHeight: 24,
    flex: 1,
    textAlignVertical: 'top',
  },
});
```

スマホ画面で確認すると、こんな感じ。
![図1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2284107/4188c68e-424b-8632-97f1-45acb4c04991.jpeg)
カーソルを合わせると、キーボードが出てくる。
![図2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2284107/169956dc-1163-58f7-5161-40b2689f8119.jpeg)

キーボードが出ると、右下にあったボタンが隠れてしまう。
これを回避するためにKeyboardAvoidingViewという便利な機能がある。

## KeyboardAvoidingView
ReactNativeにはKeyboardAvoidingViewという便利な機能がある。
それを使うと、

MemoEditScreen.jsx

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

import AppBar from '../components/AppBar';
import CircleButton from '../components/CircleButton';

// eslint-disable-next-line react/function-component-definition
export default function MemoEditScreen() {
  return (
    <KeyboardAvoidingView style={styles.container} behavior="height">
      <AppBar />

      <View style={styles.inputContainer}>
        <TextInput value="買い物" multiline style={styles.input} />
      </View>

      <CircleButton name="check" />
    </KeyboardAvoidingView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  inputContainer: {
    paddingVertical: 30,
    paddingHorizontal: 24,
    flex: 1,
  },
  input: {
    fontSize: 20,
    lineHeight: 24,
    flex: 1,
    textAlignVertical: 'top',
  },
});
```
![図1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2284107/782950ee-45b5-88df-f274-9eb1c9633354.jpeg)

ボタンがキーボードで隠れなくなる。


# KeybordAvoindingViewのバグ
キーボードのタイプを変更すると、ボタンが消えてしまうバグがある。
それを回避するために、
src/componentsの中にKeyboardSafeView.jsxファイルを作成する。

KeyboardSafeView.jsx

```jsx
import React, { useRef, useState, useEffect } from 'react';
import {
  Keyboard, Dimensions, Animated, ViewPropTypes,
} from 'react-native';
// eslint-disable-next-line import/no-extraneous-dependencies
import { node } from 'prop-types';

// eslint-disable-next-line react/function-component-definition
export default function KeyboardSafeView({ children, style }) {
  const initialViewHeight = useRef(null);
  const animatedViewHeight = useRef(null);
  const [viewHeight, setViewHeight] = useState(null);

  useEffect(() => {
    Keyboard.addListener('keyboardDidShow', handleShow);
    Keyboard.addListener('keyboardDidHide', handleHide);
    return () => {
      Keyboard.removeListener('keyboardDidShow', handleShow);
      Keyboard.removeListener('keyboardDidHide', handleHide);
    };
  }, []);

  useEffect(() => {
    if ([initialViewHeight, animatedViewHeight, viewHeight].some((val) => val === null)) { return; }
    // height is not supported with useNativeDriver: true
    // https://github.com/react-native-community/react-native-modal/issues/163
    if (viewHeight === initialViewHeight.current) {
      Animated.timing(
        animatedViewHeight.current,
        { toValue: initialViewHeight.current, duration: 300, useNativeDriver: false },
      ).start();
    } else {
      Animated.timing(
        animatedViewHeight.current,
        { toValue: viewHeight, duration: 300, useNativeDriver: false },
      ).start();
    }
  }, [viewHeight]);

  const handleShow = ({ endCoordinates }) => {
    if (endCoordinates.height && initialViewHeight.current) {
      const keyboardHeight = Dimensions.get('window').height - endCoordinates.screenY;
      setViewHeight(initialViewHeight.current - keyboardHeight);
    }
  };

  const handleHide = () => {
    setViewHeight(initialViewHeight.current);
  };

  const handleLayout = ({ nativeEvent }) => {
    if (!initialViewHeight.current) {
      const { height } = nativeEvent.layout;
      // keep viewHeight as null not to trigger useEffect on mounting.
      // Don't do this: setViewHeight(height);
      initialViewHeight.current = height;
      animatedViewHeight.current = new Animated.Value(height);
    }
  };

  const animatedStyle = viewHeight ? {
    height: animatedViewHeight.current,
    flex: 0,
  } : {};
  return (
    <Animated.View style={[style, animatedStyle]} onLayout={handleLayout}>
      {children}
    </Animated.View>
  );
}

KeyboardSafeView.propTypes = {
  children: node.isRequired,
  style: ViewPropTypes.style,
};

KeyboardSafeView.defaultProps = {
  style: null,
};
```

KeyboadAvoidingViewの代わりに、このKeyboardSafeViewを使うと
キーボードのタイプを変更してもボタンが消えなくなる。