reactでpropsを使用し、データの受け渡しを行う

### はじめに
個人開発メモ📝
(11/19更新）

### 今回の課題
コンポーネント間で役割を分けたので、データの受け渡しや処理の呼び出しを行えるようにする。

### データの受け渡し設計と役割分担
メインコンポーネント⇨ ビューとフォームページの呼び出し
フォームコンポーネント⇨ フォーム入力、送信
機能コンポーネント⇨ バックエンドとのやり取り

##### （親）メインコンポーネント
カレンダーを表示し、フォームコンポーネントを呼び出す
フォームコンポーネントからレスポンスデータを受け取り、ビューに表示

　　⬆︎　（データを渡す）
##### （子）フォームコンポーネント
入力されたフォームデータを収集し、機能コンポーネントに渡す

　　⬇︎　（処理の呼び出し）
##### 機能用コンポーネント
バックエンドとのリクエスト・レスポンスのやり取り

### ①フォーム送信イベント内で、別のコンポーネントの関数を呼び出す
```※修正前　schedule.jsx（フォームコンポーネント）
const ScheduleForm = () => {
  // フォームデータ変数定義
  const [scheduleFormData, setScheduleFormData] = useState({
    date: '',
    requirement: '',
    memo: '',
  });

  // フォーム送信イベントの制御
  const [submitEvent, setSubmitEvent] = useState(false);

  // 入力内容の反映
  const handleChange = (event) => {
    const { name, value } = event.target;
    setScheduleFormData({ ...scheduleFormData, [name]: value});
  };

  // props経由で関数を使用し、Feature.jsxに値を渡す
  const handleInputSubmit = async(event) => {
    event.preventDefault();
    handleAddEvent(scheduleFormData);
    setSubmitEvent(true);
  };

  // 機能コンポーネントから渡ってきたデータを取得する
  const handleAddEvent = async(data) => {
    console.log('渡ってきたデータ：', data);
  };

 // ビュー部分
  return(
    <div>
      <div className={ Schedule.schedule }>
        <form className={ Schedule.schedule__form }
              onSubmit={ handleInputSubmit }
        >
        
          以下省略
          
          {/* 機能コンポーネントにフォームデータをまとめて渡す */}
          <FeatureOfForm formData={ scheduleFormData } handleAddEvent={ handleAddEvent } submitEvent={ submitEvent } />
        </form>
      </div>
    </div>
  );
};

export default ScheduleForm;
```

```※修正後schedule.jsxx
import React, { useState } from 'react';
import Schedule from "../scss/schedule.module.scss";
import { postFormData } from './Feature';

const ScheduleForm = ({ passToResponseData }) => {
  // フォームデータ変数定義
  const [scheduleFormData, setScheduleFormData] = useState({
    date: '',
    requirement: '',
    memo: '',
  });

  // 入力内容の反映
  const handleChange = (event) => {
    const { name, value } = event.target;
    setScheduleFormData({ ...scheduleFormData, [name]: value});
  };

  // Feature.jsxにあるpostFormData()を呼び出す
  const handleInputSubmit = async(event) => {
    event.preventDefault();
    // csrfトークン取得
    const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
    // responseに、バックエンドからのレスポンスが代入される
    const response = await postFormData({
      csrfToken,
      data: scheduleFormData,
    });
    if(response !=null) {
      console.log('渡ってきたデータ：', response);
      await passToResponseData(response);
    } else {
      console.log('データがありません');
    }
  };

  return(
    <div>
      <div className={ Schedule.schedule }>
        <form className={ Schedule.schedule__form }
              onSubmit={ handleInputSubmit }
        >
          <label className={ Schedule.schedule__label }>
            日付：
            <input  type="date"
                    name="date"
                    placeholder="日付の選択をしてください"
                    className={ Schedule.schedule__content }
                    onChange={ handleChange }
            />
          </label>
          <label className={ Schedule.schedule__label }>
            要件：
            <input  type="text"
                    name="requirement"
                    placeholder="内容を入力してください"
                    className={ Schedule.schedule__content }
                    onChange={ handleChange }
            />
          </label>
          <label className={ Schedule.schedule__label }>
            メモ：
            <textarea className={ Schedule.schedule__content }
                      name="memo"
                      onChange={ handleChange }
            >
            </textarea>
          </label>
          <div>
            <input  type="submit"
                    value="作成"
            />
          </div>
        </form>
      </div>
    </div>
  );
};

export default ScheduleForm;
```
修正点
・propsで値のやり取りせず、機能コンポーネント内の関数を呼び出す

```※修正前　Feature.jsx（機能コンポーネント）
const FeatureOfForm = ({ formData, handleAddEvent, submitEvent }) => {
  // レスポンスデータを格納する変数定義
  const [updateEvents, setUpdateEvents] = useState('');
 
  // csrfトークン取得
  const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');

  // APIルート
  const scheduleApiUrl = '/api/schedule';

  // フォームデータの送信・レスポンスの取得
  const handleSubmit = async() => {
    try {
      const response = await fetch(scheduleApiUrl, {
        method: 'POST',
        headers: {
          'CONTENT-TYPE': 'application/json',
          'X-CSRF-TOKEN': csrfToken,
        },
        body: JSON.stringify(formData)
      });
      if(!response.ok) {
        throw new Error('エラーが出ました');
      }
      const data = await response.json();
      setUpdateEvents(data);

      // 子（Schedule.jsx）コンポーネントの関数を使用する
      handleAddEvent(data);
    } catch(error) {
      console.log(error);
    }
  };

  // Schedule.jsxで送信イベントが行われた場合のみ、バックエンドとのやり取りを行う
  useEffect(() => {
   handleSubmit(formData);
  }, [submitEvent]);
};

export default FeatureOfForm;
```

```※修正後　Feature.jsx
// APIルート
const scheduleApiUrl = '/api/schedule';

// フォームデータの送信・レスポンスの取得
export const postFormData = async({ csrfToken, data }) => {
  try {
    const response = await fetch(scheduleApiUrl, {
      method: 'POST',
      headers: {
        'CONTENT-TYPE': 'application/json',
        'X-CSRF-TOKEN': csrfToken,
      },
      body: JSON.stringify(data)
    });
    if(!response.ok) {
      throw new Error('エラーが出ました');
    };
    return response.json();
  } catch(error) {
      console.log(error);
      return undefined;
  };
};
```

修正点
・propsのやり取りがなくなったので、余計な変数が無くなる

### ②フォームコンポーネントからメインコンポーネントにデータを渡す

``` Schedule.jsx　(該当部分のみ）
const ScheduleForm = ({ passToResponseData }) => {

　省略
 
const handleInputSubmit = async(event) => {
    event.preventDefault();
    // csrfトークン取得
    const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
    // responseに、バックエンドからのレスポンスが代入される
    const response = await postFormData({
      csrfToken,
      data: scheduleFormData,
    });
    if(response !=null) {
      console.log('渡ってきたデータ：', response);
      await passToResponseData(response);
    } else {
      console.log('データがありません');
    }
  };
  
　省略
 
```
メインコンポーネント（親）のpassToResponseDataを使用し、レスポンスデータを渡す。

``` Calender.jsx　(該当部分のみ）
const CalenderUserPage = React.memo(function() {

　省略

// Schedule.jsxから値が返ってくるので取得・更新し、フォームを閉じる
  const [responseViewData, setResponseViewData] = useState(null);
  const passToResponseData = async(data) => {
    setResponseViewData(data);
    setOpenForm(!openForm);
  };

　省略

{/* 取得したデータを反映させる */}
<div className={ Calender.calender__content }>
  { responseViewData && (
    <p className={ Calender.calender__detail }>{ responseViewData.validate.requirement }</p>
 　　)}
</div>

省略

{/* 予定作成フォームコンポーネントの呼び出し */}
<div>
 　<button className={ Calender.calender__btn }
         　onClick={ handleClick }
 　>+
 </button>
 { openForm && <ScheduleForm passToResponseData={ passToResponseData } />}
</div>
};
```

フォームコンポーネントから渡ってきた値を変数にセットし、ビューに反映させる。

useEffectで処理の呼び出しを記述していましたが、呼び出すタイミングなど自分でも分からなくなっていたので、本当に使用する必要があるのか？とこれからはよく考えて記述しようと思います。
※コメントくださった方、ありがとうございました。