はじめに
おはようございます。こんにちは。こんばんは。
Watataku です。
今回の記事は React を使って簡単に LP を制作してみたいと思います。
自分的に React を学びたての方で、アウトプットに「何を作ろうか」と困っている方におすすめだと思っています。
ちなみに今回は TypeScript で書いていきます。
React で LP を作る
<div class="card">
<div class="inner">
<img src="./images/image01.jpeg" class="img" />
<div class="description">
<h1>コンテンツ1</h1>
<p>
テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト
</p>
</div>
</div>
</div>
<div class="card">
<div class="inner">
<img src="./images/image02.jpeg" class="img" />
<div class="description">
<h1>コンテンツ2</h1>
<p>
テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト
</p>
</div>
</div>
</div>
<div class="card">
<div class="inner">
<img src="./images/image03.jpeg" class="img" />
<div class="description">
<h1>コンテンツ3</h1>
<p>
テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト
</p>
</div>
</div>
</div>
コンテンツが増えていくたびに上記のような<div>
地獄になりコードが読みにくくなります。
なのでそれを React で解決させます。
解決法
データ(data..json)とビュー(Card.tsx)を分け、ループさせる。
[
{
"title": "コンテンツ1",
"description": "テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト",
"imagePath": "./images/image01.jpeg"
},
{
"title": "コンテンツ2",
"description": "テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト",
"imagePath": "./images/image02.jpeg"
},
{
"title": "コンテンツ3",
"description": "テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト ",
"imagePath": "./images/image03.jpeg"
}
]
import React from "react"
import styles from "./Card.module.css"
type Props = {
title: string
description: string
imagePath: string
}
const Card = (props: Props) => {
return(
<section className={styles.contents}>
<div className={styles.inner}>
<img src={props.imagePath} alt={props.title} className={styles.img} />
<div className={styles.description}>
<h1 className={styles.title}>{props.title}</h1>
<p className={styles.text}>{props.description}</p>
</div>
</div>
</section>
)
}
export default Card;
import React, {ComponentProps} from 'react';
import data from "./data.json"
const App = () => {
return(
<div className="card">
{data.map((item: ComponentProps<typeof Card>, index: number) => {
return(
<Card title={item.title} description={item.description} imagePath={item.imagePath} key={index} />
)
})}
</div>
)
}
このようにdata.json(データ)、Card.tsx(ビュー)
を分けることにより、
効率が良くなりまたコードの可読性も良くなります。
スクロールアニメーションの実装
LP でよく見かけると思いますが、スクロールしていくと要素が出現していくあれです。
これを React で実装するためにライブラリを入れていきます。
animate.cssとreact-inview-monitorです。
$ npm install animate.css react-inview-monitor
import React from "react"
import styles from "./Card.module.css"
import InViewMonitor from "react-inview-monitor"
type Props = {
title: string
description: string
imagePath: string
}
const Card = (props: Props) => {
return(
<section className={styles.contents}>
<div className={styles.inner}>
<InViewMonitor classNameNotInView='hidden' classNameInView='animate__animated animate__fadeInLeft slower'>
<img src={props.imagePath} alt={props.title} className={styles.img} />
</InViewMonitor>
<div className={styles.description}>
<InViewMonitor classNameNotInView='hidden' classNameInView='animate__animated animate__fadeInRight slower'>
<h1 className={styles.title}>{props.title}</h1>
</InViewMonitor>
<InViewMonitor classNameNotInView='hidden' classNameInView='animate__animated animate__fadeInUp slower'>
<p className={styles.text}>{props.description}</p>
</InViewMonitor>
</div>
</div>
</section>
)
}
export default Card;
このようにreact-inview-monitor モジュールを読み込み、スクロールアニメーションさせたい要素にInViewMonitor コンポーネントで囲んでやれば簡単にスクロールアニメーションが実装できます。
ただし、今回の場合react-inview-monitor モジュールを読み込んだ時点でエラーが出ます。なぜでしょう?
それは今回 Typescript で書いているため、「型がない」とエラーが出ます。
外部ライブラリを使う時の型定義がない時の解決法
2種類解決策があります。
まずひちつめにtsconfig.jsonで**"noImplicitAny": falseとしてあげ暗黙的 any を許してあげる。
でもこれをしちゃうと Typescript で書いている意味がなくなっちゃいますよね(笑)
なので、新たに型定義ファイル(.d.ts)**を作ってやる。
declare module 'react-inview-monitor' {
export default function InViewMonitor(
children?: any,
classNameNotInView?: string,
classNameInView?: string,
classNameAboveView?: string,
intoViewMargin?: string,
onInView?: Function,
onNotInView?: Function
): JSX.Element
}
この作成したファイルをsrc/@types フォルダを作成し、その中で保存するとreact-inview-monitor モジュールが無事動くようになり、スクロールアニメーションが実装できます。
フォームの実装
お問い合わせなどフォームの実装方法です。実際のコード
const Form = () => {
const [firstName, setFirstName] = useState("");
const inputFirstName = useCallback(
(event) => {
setFirstName(event.target.value);
},
[setFirstName]
);
return (
<input
type="text"
name="firstName"
placeholder="firstNameを入力してください。"
value={firstName}
onChange={inputFirstName}
/>
);
};
テキストボックスに入力があるたびにonChange(inputFirstName)が実行されて、setFirstNameでstate(firstName)を更新してます。
つまり入力した内容がfirstNameになっています。
React ではテキストボックスを置いておくだけでは入力ができず、上記のように実装してあげないといけないのです。