1
3

More than 3 years have passed since last update.

Progate終了レベル+αの知識でReactアプリを実装

Last updated at Posted at 2020-04-13

ProgateでReactの勉強をしました。
Progate上で実装した機能をローカル環境で再現して理解を深めようと思います。

作成する機能

  • 複数のコンポーネントを組み合わせて1枚のページを作成
  • ボタンをクリックしたらテキストの中身が切り替わる
  • モーダル
  • フォーム
    • 送信をクリックするとフォーム部分だけ表示が変わる
    • 何も入力されていないとエラーを出す

creat-react-appのインストール

$ npm i -g create-react-app

プロジェクトの初期化

$ create-react-app progate-local
$ cd progate-local
$ rm -rf src
$ mkdir src src/components
$ touch src/index.js src/components/App.js
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';

ReactDOM.render(<App />, document.getElementById('root'));

src/components/App.js
import React from 'react';

class App extends React.Component {
  render() {
    return (
      <div>
        <p>Hello World</p>
      </div>
    );
  }
}

export default App;

複数のコンポーネントを組み合わせてページを作成

$ touch src/components/Header.js src/components/Footer.js
src/components/App.js
import React from 'react';
import Header from './Header';
import Footer from './Footer'

class App extends React.Component {
  render() {
    return (
      <div>
        <Header />
        <Footer />
      </div>
    );
  }
}

export default App;
src/components/Header.js
import React from 'react';

class Header extends React.Component {
  render() {
    return (
      <header>
        <h1>Header!!!</h1>
      </header>
    );
  }
}

export default Header;
src/components/Footer.js
import React from 'react';

class Footer extends React.Component {
  render() {
    return (
      <footer>
        <small>Footer!!!</small>
      </footer>
    );
  }
}

export default Footer;

ボタンクリックでテキストが切り替わる

$ touch src/components/ChangeText.js
src/components/ChangeText.js
import React from "react";

class ChangeText extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      name: '茂野吾郎'
    }
  }
  changeName(name) {
    this.setState({
      name: name
    })
  }
  render() {
    return (
      <div>
        <h2>こんにちは{this.state.name}さん</h2>
        <button onClick={ () => this.changeName('茂野吾郎') }>茂野吾郎に変更</button>
        <button onClick={ () => this.changeName('佐藤寿也') }>佐藤寿也に変更</button>
      </div>
    );
  }
}

export default ChangeText;
src/components/App.js
import React from 'react';
import Header from './Header';
import Footer from './Footer'
+ import ChangeText from './ChangeText'

class App extends React.Component {
  render() {
    return (
      <div>
        <Header />
+         <ChangeText />
        <Footer />
      </div>
    );
  }
}

export default App;

ポイント

  • ChangeText.jsというコンポーネントを作成
  • nameという名前のstateを定義
  • nameの値を出力する
  • 引数にとった文字列でnameを更新するメソッド(changeName)を作成
  • ボタンを作成し、クリック時にchangeNameメソッドを発火

モーダルを作成

$ touch src/components/Modal.js
$ touch src/modal.css
src/components/Modal.js
import React from "react";
import '../modal.css'

class Modal extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isModalOpen: false,
    };
  }
  toggleModal() {
    this.setState({
      isModalOpen: !this.state.isModalOpen,
    });
  }
  render() {
    let modal
    if(this.state.isModalOpen){
      modal = (
        <div className='modal'>
          <div className='modal_content'>
            <p>モーダル!!!</p>
            <button onClick={ ()=> { this.toggleModal() } }>閉じる</button>
          </div>
        </div>
      )
    }
    return(
      <div>
        <button onClick={ () => { this.toggleModal() } }>モーダルを開く</button>
        {modal}
      </div>
    )
  }
}

export default Modal;
src/modal.css
.modal {
  background: rgba(0,0,0,0.7);
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0;
  left: 0;
}

.modal_content {
  background: #fff;
  padding: 20px;
  width: 70%;
  height: auto;
  margin: 100px auto 0;
}
src/components/App.js
import React from 'react';
import Header from './Header';
import Footer from './Footer'
import ChangeText from './ChangeText'
+ import Modal from './Modal'

class App extends React.Component {
  render() {
    return (
      <div>
        <Header />
        <ChangeText />
+        <Modal />
        <Footer />
      </div>
    );
  }
}

export default App;

ポイント

  • Modal.jsというコンポーネントを作成
  • isModalOpenという、真偽値をとるstateを定義
  • modalという変数を定義し、isModalOpenがtrueの時だけ値(html)が入るようにする
  • isModalOpenの値を変更するメソッド(toggleModal)を作成
  • クリック時にtoggleModalが発火するボタンを、モーダル内とモーダル外にそれぞれ設置する

モーダルを繰り返し表示

src/components/App.js
import React from "react";
import Header from "./Header";
import Footer from "./Footer";
import ChangeText from "./ChangeText";
import Modal from "./Modal";

class App extends React.Component {
  render() {
    const playerList = [
      {
        position: "ピッチャー",
        name: "茂野吾郎",
      },
      {
        position: "キャッチャー",
        name: "佐藤寿也",
      },
      {
        position: "ファースト",
        name: "ギブソンjr",
      },
    ];
    return (
      <div>
        <Header />
        <ChangeText />
        <h3>モーダル</h3>
        {playerList.map((playerItem, index) => {
          return (
            <Modal
              key={index}
              position={playerItem.position}
              name={playerItem.name}
            />
          );
        })}
        <Footer />
      </div>
    );
  }
}

export default App;
src/components/Modal.js
//省略
  render() {
    let modal
    if(this.state.isModalOpen){
      modal = (
        <div className='modal'>
          <div className='modal_content'>
            <p>{this.props.position}</p>
            <p>{this.props.name}</p>
            <button onClick={ ()=> { this.toggleModal() } }>閉じる</button>
          </div>
        </div>
      )
    }
    return(
      <div>
        <button onClick={ () => { this.toggleModal() } }>{this.props.position}</button>
        {modal}
      </div>
    )
  }
}

export default Modal;

ポイント

  • App.js内に配列(playerList)を定義する
  • playerListmapメソッドを適用し、ループの中でModalコンポーネントを呼び出す
  • 呼び出す時に変数を受け渡す
  • 受け渡された値はModal.js内でthis.props.hogeで受け取る

フォームを作成

$ touch src/components/Form.js
src/components/Form.js
import React from "react";

class Form extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isSubmitted: false,
      email: "",
      hasEmailError: false,
      content: "",
      hasContentError: false,
    };
  }
  handleEmailChange(event) {
    const inputValue = event.target.value;
    const isEmpty = event.target.value === "";
    this.setState({
      email: inputValue,
      hasEmailError: isEmpty,
    });
  }
  handleContentChange(event) {
    const inputValue = event.target.value;
    const isEmpty = event.target.value === "";
    this.setState({
      content: inputValue,
      hasContentError: isEmpty,
    });
  }
  handleSubmit() {
    this.setState({
      isSubmitted: true,
    });
  }
  render() {
    let emailErrorText;
    if (this.state.hasEmailError) {
      emailErrorText = <p>メールアドレスを入力してください</p>;
    }
    let contentErrorText;
    if (this.state.hasContentError) {
      contentErrorText = <p>問合せ内容を入力してください</p>;
    }
    let contactForm;
    if (this.state.isSubmitted) {
      contactForm = <p>送信完了</p>;
    } else {
      contactForm = (
        <div>
          <p>メールアドレス</p>
          <input
            type="text"
            value={this.state.email}
            onChange={(event) => {
              this.handleEmailChange(event);
            }}
          />
          {emailErrorText}
          <p>問合せ内容</p>
          <textarea
            value={this.state.content}
            onChange={(event) => {
              this.handleContentChange(event);
            }}
          />
          { contentErrorText }
          <input
            type="submit"
            value="送信"
            onClick={() => {
              this.handleSubmit();
            }}
          />
        </div>
      );
    }
    return <div>{contactForm}</div>;
  }
}

export default Form;
src/components/App.js
import React from "react";
import Header from "./Header";
import Footer from "./Footer";
import ChangeText from "./ChangeText";
import Modal from "./Modal";
+ import Form from "./Form";

class App extends React.Component {
  render() {
省略
    return (
      <div>
        <Header />
        <ChangeText />
        <h3>モーダル</h3>
        {playerList.map((playerItem, index) => {
          return (
            <Modal
              key={index}
              position={playerItem.position}
              name={playerItem.name}
            />
          );
        })}
+        <Form />
        <Footer />
      </div>
    );
  }
}

export default App;

ポイント

  • Form.jsというコンポーネントを作成
  • isSubmittedcontenthasContentErrorというstateを定義
  • form入力欄のvalueにcontentの値を設定する
  • contactFormという変数を定義し、isSubmittedの値に応じて表示を切り分ける
  • isSubmittedの値を変更するメソッド(handleSubmit)を定義し、送信ボタンクリック時に発火させる
  • 入力欄に入力された値に応じてstateを変更するメソッド(handleContentChange)を作成
  • 入力欄の値が変更された時、handleContentChangeを発火させる
  • ContentErrorTextという変数を定義し、hasContentErrorがtrueの時だけ値(html)が入るようにする
  • 入力欄の下にContentErrorTextを出力
  • email入力欄に関しても、contentと同様の設定をする

参考

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3