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
)を定義する -
playerList
にmap
メソッドを適用し、ループの中で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
というコンポーネントを作成 -
isSubmitted
、content
、hasContentError
というstateを定義 - form入力欄のvalueに
content
の値を設定する -
contactForm
という変数を定義し、isSubmitted
の値に応じて表示を切り分ける -
isSubmitted
の値を変更するメソッド(handleSubmit
)を定義し、送信ボタンクリック時に発火させる - 入力欄に入力された値に応じてstateを変更するメソッド(
handleContentChange
)を作成 - 入力欄の値が変更された時、
handleContentChange
を発火させる -
ContentErrorText
という変数を定義し、hasContentError
がtrueの時だけ値(html)が入るようにする - 入力欄の下に
ContentErrorText
を出力 - email入力欄に関しても、contentと同様の設定をする
##参考