11
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

React入門 未経験から1週間でReactをマスターする #05. フォームと親子間のデータのやり取り

目次

1. Reactの新規プロジェクトの立ち上げ
2. コンポーネントのプロパティ(props)とステート(state)
3. Class Components と Function Components
4. 条件分岐 (if) と繰り返し (loop)
5. フォームと親子間のデータのやり取り←今ここ
6. コンポーネントのライフサイクル
7. スタイル (準備中)
8. Higher-Order Component (準備中)
9. Portalを利用したモーダル (準備中)
10. refによるエレメントの取得 (準備中)
11. Contextを利用したテーマの変更 (準備中)

今回の学習内容

今回は、

  • Reactでのフォームの利用方法
  • 親子間でのデータのやりとり

をやっていきます。

YouTubeでの解説動画

YouTubeでも解説しています。
動画で確認したい方はこちらもどうぞ。
【YouTube動画】 未経験から1週間でをマスターするReact入門 #05. フォームと親子間のデータのやり取り
未経験から1週間でをマスターするReact入門 #05. フォームと親子間のデータのやり取り

この記事のソースコード

ソースコードはGitHubで公開しています。

今回のコミット一覧↓

スクリーンショット 2020-12-01 13.16.00.png

フォームの利用

まずはReactでフォームを利用してみます。
Form.js にフォームを入れます。

このWebアプリでは、言語のリストと追加を行うアプリにしたいので、新しい言語を追加するフォームを作ります。

src/Form.js
import { useState } from 'react'; // 追加

export const Form = () => {
  const [text, setText] = useState(''); // 追加

  const submitForm = (e) => { // 追加
    e.preventDefault();
    console.log(`submitForm(${text})`);
  }

  return (
    <div>
      { /* 変更 */ }
      <h4>新しい言語の追加</h4>
      <form onSubmit={submitForm}>
        <div>
          <input type="text" value={text} onChange={(e) => setText(e.target.value)} />
        </div>
        <div>
          <button>追加</button>
        </div>
      </form>
    </div>
  )
}

コードの説明

  const [text, setText] = useState(''); // 追加

まずここで、 text という state を追加しています。

  const submitForm = (e) => { // 追加
    e.preventDefault();
    console.log(`submitForm(${text})`);
  }

次に、 form をサブミットされたときの関数を定義しました。

ここでは、 preventDefault() をしてコンソールにログを表示するようにしています。

<h4>新しい言語の追加</h4>
<form onSubmit={submitForm}>
  <div>
    <input type="text" value={text} onChange={(e) => setText(e.target.value)} />
  </div>
  <div>
    <button>追加</button>
  </div>
</form>

form タグに onSubmit を定義しています。これで form がサブミットされると 先程定義した submitForm 関数が呼び出されます。

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

次にこの input タグですが、これは text state を値としてもち、onChange を利用することで変更検知を行って text stateを setText を行うことで変更しています。

こうすることで、 text を常に input の内容と一致させることができます。

実際に動かしてみると下図のようになります。

7c9b5ec202b5788520e1189e570a9f41.gif

ここまでのコミット

親子間のデータのやりとり

次に、フォームに入力されるとリスト側の言語が増えるようにしてみましょう。

今は List.js が言語データを管理しているのdせうが、言語データを App.js が管理するようにします。
そこでまず、前回 List.js で定義していた LANGUAGE 定数を別ファイル( src/const/languages.js )に移します。

src/const/languages.js
export const LANGUAGES = [
  'JavaScript',
  'C++',
  'Ruby',
  'Java',
  'PHP',
  'Go'
];

それを利用して、 App.js を修正します。

src/App.js
import { useState } from 'react';
import { List } from "./List";
import { Form } from "./Form";
import { LANGUAGES } from "./const/languages"; // 追加

function App() {
  const [tab, setTab] = useState('list');
  const [langs, setLangs] = useState(LANGUAGES); // 追加

  return (
    <div>
      <header>
        <ul>
          <li onClick={() => setTab('list')}>リスト</li>
          <li onClick={() => setTab('form')}>フォーム</li>
        </ul>
      </header>
      <hr />
      {
        tab === 'list' ? <List /> : <Form />
      }
    </div>
  );
}

export default App;

言語データを管理するために langs という state を定義しました。

Form.js => App.js のデータのやり取り

次に、子コンポーネントである Form.js から App.js へのデータの受け渡しをします。

子から親へのデータの受け渡しは、 props を利用します。
具体的には、props に関数への参照を渡すことで、 子コンポーネントからその関数を呼び出してもらいます。

src/App.js
import { useState } from 'react';
import { List } from "./List";
import { Form } from "./Form";
import { LANGUAGES } from "./const/languages";

function App() {
  const [tab, setTab] = useState('list');
  const [langs, setLangs] = useState(LANGUAGES);

  const addLang = (lang) => { // 追加
    console.log(lang);
    setLangs([...langs, lang])
  }

  return (
    <div>
      <header>
        <ul>
          <li onClick={() => setTab('list')}>リスト</li>
          <li onClick={() => setTab('form')}>フォーム</li>
        </ul>
      </header>
      <hr />
      {
        tab === 'list' ? <List /> : <Form onAddLang={addLang}/> // 変更
      }
    </div>
  );
}

export default App;

App.js 側に addLang 関数を作成し、 Form.js のプロパティとして渡してみました。
これを Form.js から呼び出してみます。

src/Form.js
import { useState } from 'react';

export const Form = ({ onAddLang }) => { // 変更
  const [text, setText] = useState('');

  const submitForm = (e) => {
    e.preventDefault();
    onAddLang(text); // 変更
  }

  return (
    <div>
      <h4>新しい言語の追加</h4>
      <form onSubmit={submitForm}>
        <div>
          <input type="text" value={text} onChange={(e) => setText(e.target.value)} />
        </div>
        <div>
          <button>追加</button>
        </div>
      </form>
    </div>
  )
}

コード解説

Form.js では、 onAddLang という props を追加しています。これは関数への参照になります。
submitForm 関数内で onAddLang 関数を呼び出すことにより、 App.js でイベントを起こします。

App.js では、

 <Form onAddLang={addLang}/>

と定義していますので、 Form.js から onAddLang が呼ばれると、 App.js 内ので addLang 関数が呼ばれることになります。

  const addLang = (lang) => { // 追加
    console.log(lang);
    setLangs([...langs, lang])
  }

addLang 関数では、 App.jslangs に対してデータを追加するようになっているので、データが追加されます。

実際に動かしてみる

実際に動かしてみて、 Form.js から App.js の関数が呼ばれていることを確認します。

今は App.js 内でコンソールに渡された値を表示することで確認できます。

a304ddb31fd27e65e4b1145478a12b19.gif

追加された言語をリストへ反映

次に、追加された言語を List.js に反映してみます。

src/App.js
import { useState } from 'react';
import { List } from "./List";
import { Form } from "./Form";
import { LANGUAGES } from "./const/languages";

function App() {
  const [tab, setTab] = useState('list');
  const [langs, setLangs] = useState(LANGUAGES);

  const addLang = (lang) => {
    setLangs([...langs, lang]);
    setTab('list'); // 追加
  }

  return (
    <div>
      <header>
        <ul>
          <li onClick={() => setTab('list')}>リスト</li>
          <li onClick={() => setTab('form')}>フォーム</li>
        </ul>
      </header>
      <hr />
      {
        tab === 'list' ? <List langs={langs} /> : <Form onAddLang={addLang}/> // 変更
      }
    </div>
  );
}

export default App;

フォームから言語が追加されたら List.js を表示するために、addLang 関数で追加されたらタブも変更するようにしました。
また、 List.jslangs stateを渡すようにしています。

次に、 List.js でこの渡された langs を利用するようにしましょう。

export const List = ({ langs }) => { // 変更
  return (
    <div>
      {
        langs.map((lang, index) => { // 変更
          return <div key={index}>{ lang }</div>
        })
      }
    </div>
  )
}

List.js からは、 src/const/languages.js に移動した LANGUAGES を削除しました。
また、 props から langs を受け取り、それを利用するようにしています。

これにより、下図のようにフォームで追加された内容が List.js に反映されるようになりました。

bbd3365b2696cd719367109b84227851.gif

今日やったこと

JSXでのフォームの利用

<form onSubmit={submitFunc}>
  <input type="text" value={text} onChange={(e) => setText(e.target.value)} />
  <button>ボタン</button>
</form>

JSXでフォームを利用するためには、 onSubmit イベントで関数を呼び出します。
input 属性は、 valueonChange を利用して状態を管理します。

親子間のデータのやり取り

子コンポーネント=>親コンポーネント

function Parent() {
  const testFunc = () => {}
  return <Child parentFunc={testFunc} />
}

function Child({ parentFunc }) {
  return <div onClick={() => parentFunc(text)}>テスト</div>
}

子コンポーネントから親コンポーネントにデータを渡すには、 props に渡された関数を子コンポーネントから呼び出すようにします。

親コンポーネント=>子コンポーネント

function Parent() {
  const test = 'テストデータ';
  return <Child parentData={test} />
}

function Child({ parentData }) {
  return <div>{ parentData }</div>
}

親コンポーネントから子コンポーネントにデータを渡すには、 props でデータを渡します。

Class Components


Class Components でのコードはこちら

App.js

  • stateの追加
  • List.jsへlangsを渡す
  • Form.jsから新しいlangを受け取る

を追加しています。

src/App.js
import React from 'react';
import { List } from "./List";
import { Form } from "./Form";
import { LANGUAGES } from "./const/languages";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      tab: 'list',
      langs: LANGUAGES,
    };
  }
  addLang(lang) {
    this.setState({
      langs: [...this.state.langs, lang],
      tab: 'list',
    });
  }
  render() {
    const { tab, langs } = this.state;
    return (
      <div>
        <header>
          <ul>
            <li onClick={() => this.setState({ tab: 'list' })}>リスト</li>
            <li onClick={() => this.setState({ tab: 'form' })}>フォーム</li>
          </ul>
        </header>
        <hr />
        {
          tab === 'list' ? <List langs={langs} /> : <Form onAddLang={(lang) => this.addLang(lang)}/>
        }
      </div>
    )
  }
}

export default App;

Form.js

Form.js は全体的に変更します。
this のことをケアしてアロー関数で書くことに注意してください。

src/Form.js
import React from 'react';

export class Form extends React.Component {
  constructor(props) {
    super(props);
    this.state = { text: '' }
  }
  submitForm(e) {
    e.preventDefault();
    this.props.onAddLang(this.state.text);
  }

  render() {
    const { text } = this.state;
    return (
      <div>
        <h4>新しい言語の追加</h4>
        <form onSubmit={(e) => this.submitForm(e)}>
          <div>
            <input type="text" value={text} onChange={(e) => this.setState({ text: e.target.value })} />
          </div>
          <div>
            <button>追加</button>
          </div>
        </form>
      </div>
    )
  }
}

List.js

src/List.js
import React from 'react';
// LANGUAGES定数を削除

export class List extends React.Component {
  render() {
    const { langs } = this.props; // propsからlangsを利用
    return (
      <div>
        {
          langs.map((lang, index) => {
            return <div key={index}>{ lang }</div>
          })
        }
      </div>
    )
  }
}

languages.js

ファイルを追加

src/const/languages.js
export const LANGUAGES = [
  'JavaScript',
  'C++',
  'Ruby',
  'Java',
  'PHP',
  'Go'
];


こちらのコミットの方が変更点がわかりやすく良いかもしれません。

おわりに

これで、 Form.jsApp.js の子コンポーネントから親コンポーネントへのデータ渡し、および App.jsList.js の親コンポーネントから子コンポーネントへのデータ渡しを実装できました。

次の記事

React入門 未経験から1週間でReactをマスターする #06. コンポーネントのライフサイクル
https://qiita.com/yassun-youtube/items/545e7d9c98fe919dc394

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
11
Help us understand the problem. What are the problem?