はじめに
①の記事:https://qiita.com/atsu123456789/items/c055959b288f53841324
③の記事:https://qiita.com/atsu123456789/items/132ecdb17f0ad2a175c0
前回の続きです。
ようやくReactとはなんたるか、どういう風に動かすのかの初歩についての記事になります。
訳あって前回のUdemyの講座とは別の講座の内容を参考にしています。
一通りやってみましたけど、React便利っすね~
こりゃ人気なわけだわ
対象者
- 基本的なプログラミングの考え方が分かる(メソッドなど)
- 最低限のJavascriptが分かる(自信ない方は僕の①の記事へGo)
- 最低限のHTML、CSSが分かる
- ReactやNext.jsに興味がある
コンポーネントとは
画面の各構成要素をReactで定義したもの
- 再利用性の向上(コードが使いまわせる)
- 可読性の向上(コードが整理されている)
- 疎結合になる(バグを減らせる)
コンポーネントの定義
// function名の先頭は必ず大文字で!!
function Welcome() {
return <h1>Hello</h1>;
}
// アロー関数を使った書き方
const Example = () => {
return <h1>Hello</h1>;
}
// コンポーネントの実行
<Welcome />
Reactのプロジェクトの作成
$ npx create-react-app my-app
$ cd my-app
$ npm start
これでローカルホストにReactのアプリが起動する
コンポーネントを使って、HTMLを生成する
上のコマンドでReactのプロジェクトをつくると、/publicの中に以下のようなファイルがある
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
<div id="root"></div>
上の部分にHTMLを入れる場合は
import React from 'react';
import ReactDom from 'react-dom'
const App = () => {
return <h1>こんにちは</h1>;
};
ReactDom.render(<App />, document.getElementById("root"));
これで「こんにちは」というh1タグが埋め込まれる。
これはidが"root"というエレメントに関数Appの戻り値を渡しているためである。
このようなJavascriptの中でHTMLのタグを書いていくのをJSX記法という
複数のHTMLタグを戻り値として使用する場合は
import React from 'react';
import ReactDom from 'react-dom'
const App = () => {
return (
<div>
<h1>こんにちは</h1>
<p>お元気ですか?</p>
</div>
);
};
ReactDom.render(<App />, document.getElementById("root"));
// もしくはReact.Fragmentを使用
const App = () => {
return (
<React.Fragment>
<h1>こんにちは</h1>
<p>お元気ですか?</p>
</React.Fragment>
);
};
//React.Fragmentは省略可能
const App = () => {
return (
<>
<h1>こんにちは</h1>
<p>お元気ですか?</p>
</>
);
};
上記の例のように戻り値は一つのDOMである必要がある。
コンポーネントを別のファイルから呼び出す
コンポーネントは別のファイルに記述し、何度でも呼び出すことが出来る。
例えば上の例で書いたAppという関数を別のファイルに記載する
import React from 'react';
const App = () => {
return (
<>
<h1>こんにちは</h1>
<p>お元気ですか?</p>
</>
);
};
export default App;
import React from 'react';
import ReactDom from 'react-dom'
import App from './App'
ReactDom.render(<App />, document.getElementById("root"));
このようにApp.jsxで
export default [関数名];
と記述することで、外部でその関数を呼び出せるようにし、関数を使用するファイルで
import App from './App'
とすることで、コンポーネントの呼び出しを行える。(.jsxはReactの拡張子)
イベントの割り当て
コンポーネント内のボタン押下時にアラートを出すイベントを作ってみる
import React from 'react';
const App = () => {
{/* アラートを出すonClickButtonという関数を定義 */}
const onClickButton = () => alert();
return (
<>
<h1>こんにちは</h1>
<p>お元気ですか?</p>
{/* {}の中はjavascriptの記法が使える */}
<button onClick={onClickButton}>ボタン</button>
</>
);
};
export default App;
Styleを定義する
jsxファイルでCSSをいじるには、別のファイルにcssを定義するやり方もあるが、今回はjsxファイル内でcssをいじるやり方について説明する
import React from 'react';
const App = () => {
const onClickButton = () => alert();
{/* CSSを定義したcontentStyleというものを用意 */}
const contentStyle = {
color: 'blue',
fontSize: '18px'
};
return (
<>
{/* HTMLタグに直接Styleを埋め込むことも可能 */}
<h1 style={{ color: 'red' }}>こんにちは</h1>
{/* pタグのスタイルに先ほど定義したcontentStyleを埋め込む */}
<p style={contentStyle}>お元気ですか?</p>
<button onClick={onClickButton}>ボタン</button>
</>
);
};
export default App;
Props
コンポーネントに渡す引数のようなもの
動的にコンポーネントを扱うため、Propsで条件を与えてあげる
import React from 'react';
import ColorfulMessage from './components/ColorfulMessage';
const App = () => {
const onClickButton = () => alert();
return (
<>
<h1 style={{ color: 'red' }}>こんにちは</h1>
<ColorfulMessage color="blue" message="お元気ですか?" />
<ColorfulMessage color="pink" message="元気です!" />
<button onClick={onClickButton}>ボタン</button>
</>
);
};
export default App;
import React from 'react'
const ColorfulMessage = (props) => {
console.log(props);
const contentStyle = {
color: props.color,
fontSize: '18px'
};
return (
<p style={contentStyle}>{ props.message }</p>
);
};
export default ColorfulMessage;
このようにApp.jsxで引数として値を渡してあげると、その関数内では一つの引数にObject型として入るので(今回の例だとpropsと命名)、そこからcolorを抽出したいときは
props.color
のようにすれば引数で指定したcolorの中身を抽出できる。
また、コンポーネントはHTMLタグのように扱うこともでき、
import React from 'react';
import ColorfulMessage from './components/ColorfulMessage';
const App = () => {
const onClickButton = () => alert();
return (
<>
<h1 style={{ color: 'red' }}>こんにちは</h1>
<ColorfulMessage color="blue">お元気ですか?</ColorfulMessage>
<ColorfulMessage color="pink">元気です!</ColorfulMessage>
<button onClick={onClickButton}>ボタン</button>
</>
);
};
export default App;
import React from 'react'
const ColorfulMessage = (props) => {
console.log(props);
const contentStyle = {
color: props.color,
fontSize: '18px'
};
return (
<p style={contentStyle}>{ props.children }</p>
);
};
export default ColorfulMessage;
このように先ほどmessageという引数で渡した内容をHTMLタグ内にいれた。
この時、ColorfulMessage.jsxで、戻り値を
props.children
とすることで、App.jsxのHTMLタグの中身が返るようになる。
(childrenはそのDOM直下の内容が返るやつ)
ColorfulMessage.jsxをさらに簡素に書こうとすると以下のように書ける
import React from 'react'
const ColorfulMessage = (props) => {
{/* propsの中身をcolorとchildrenに分割代入 */}
const { color, children } = props;
console.log(props);
const contentStyle = {
color: color,
{/* javascriptではプロパティ名と渡す値が同じ名前の時、省略できるため以下のように書くこともできる */}
{/* color */}
fontSize: '18px'
};
return (
<p style={contentStyle}>{ children }</p>
);
};
export default ColorfulMessage;
State
各コンポーネントが持つ状態。Stateが変更されると再レンダリングされる
import React from 'react';
import { useState } from 'react';
import ColorfulMessage from './components/ColorfulMessage';
const App = () => {
const onClickCountUp = () => {
{/* setNum関数によってnumの値を更新する処理 */}
setNum(num + 1);
};
{/* Stateを定義。useStateの引数はnumを表す。
setNum関数によってnumを変更することが出来、
その際に再レンダリングが行われる */}
const [num, setNum] = useState(0);
return (
<>
<h1 style={{ color: 'red' }}>こんにちは</h1>
<ColorfulMessage color="blue">お元気ですか?</ColorfulMessage>
<ColorfulMessage color="pink">元気です!</ColorfulMessage>
<button onClick={onClickCountUp}>カウントアップ</button>
<p>{num}</p>
</>
);
};
export default App;
再レンダリングと副作用(useEffect)
Stateによって、その値が更新されると再レンダリングが起きることは前節で説明した。
ではそれによる副作用を見ていく。
import React from 'react';
import { useState } from 'react';
import ColorfulMessage from './components/ColorfulMessage';
const App = () => {
const [num, setNum] = useState(0);
const [faceShowFlag, setFaceShowFlag] = useState(true);
const onClickCountUp = () => {
setNum(num + 1);
};
const onClickSwitchShowFlag = () => {
setFaceShowFlag(!faceShowFlag);
};
if (num % 3 == 0) {
setFaceShowFlag(true);
} else {
setFaceShowFlag(false);
}
return (
<>
<h1 style={{ color: 'red' }}>こんにちは</h1>
<ColorfulMessage color="blue">お元気ですか?</ColorfulMessage>
<ColorfulMessage color="pink">元気です!</ColorfulMessage>
<button onClick={onClickCountUp}>カウントアップ</button>
<br />
<button onClick={onClickSwitchShowFlag}>on/off</button>
<p>{num}</p>
{faceShowFlag && <p>( *´艸`)</p>}
</>
);
};
export default App;
上の例は、numが3の倍数の時にfaceShowFlagがたち、
{faceShowFlag && <p>( *´艸`)</p>}
を表示するという処理である。
ただ、このままだとエラーになる。
なぜかというと、
if (num % 3 == 0) {
setFaceShowFlag(true);
} else {
setFaceShowFlag(false);
}
この部分で、numの値によってfaceShowFlagの変更を行い、再レンダリングが行われ、再び最初から読み込みが始まる。
その結果、無限ループに陥るからである。
なので以下のように毎回faceShowFlagが変更されないようにすればよい
if (num % 3 == 0) {
faceShowFlag == false && setFaceShowFlag(true);
} else {
faceShowFlag && setFaceShowFlag(false);
}
ただ現状のままだと一つ問題がある。それは"onClickSwitchShowFlag"を変更しても上の条件分岐のせいでなにも変わらなくなるということだ。
ここでuseEffectを用いて関心毎の分離を行う
useEffect
useEffectは関心毎を分離させたいときに使う
例えば上の例の条件分岐の際に
useEffect(() => {
if (num % 3 == 0) {
faceShowFlag == false && setFaceShowFlag(true);
} else {
faceShowFlag && setFaceShowFlag(false);
}
{/* 第二引数を[]にすることで、一番最初のレンダリング時のみ行いたい処理を実行することもできる */}
}, [num]);
このようにすることで、numの変更を感知したときのみ、useEffect内の処理が走るようにすることもできる。
default exportとexport
export defaultの場合
import React from 'react'
const ColorfulMessage = (props) => {
const { color, children } = props;
const contentStyle = {
color: color,
fontSize: '18px'
};
return (
<p style={contentStyle}>{ children }</p>
);
};
export default ColorfulMessage;
読み込み先のファイル
import ColorfulMessage from './components/ColorfulMessage';
・exportの場合
import React from 'react'
export const ColorfulMessage = (props) => {
const { color, children } = props;
const contentStyle = {
color: color,
fontSize: '18px'
};
return (
<p style={contentStyle}>{ children }</p>
);
};
読み込み先のファイル
import { ColorfulMessage } from './components/ColorfulMessage';
このように、exportを使う際には分割代入で、コンポーネント名を教える必要がある。
ただexport defaultと違って、一つのファイルに一つの戻り値でないため、複数のコンポーネントを定義できる。
最後に
長くなりそうなので今回はここまで。
③ではTodoアプリを作ってみるのでよかったらみてね~
参考文献