前ページでフロント・バック・データベースの接続確認が完了したので、ここからはメモアプリのUIと機能を実装していきます。

本章ではフロントエンドの検索・追加フォームから、任意のデータを検索・表示あるいは追加できるようにするのが目標です。

  1. 簡易UIの作成
  2. 準備中
  3. 準備中



簡易UIの作成

まずは簡易的なUI(User Interface)を作っていきます。UIとはすなわちアプリの概観です。

今回作成するUIに必要なコンポーネントは、次の4つです。

  • Headerコンポーネント
  • Formコンポーネント
  • Listコンポーネント

完成イメージはこんな感じ。

 

まずは、コンポーネントファイルを格納するためのmodulesディレクトリと、CSSファイルを格納するためのcssディレクトリを作成しましょう。ターミナルにてmemo-app/frontend/srcディレクトリに移動したら、次のコマンドを実行してください。

mkdir modules
mkdir css

Headerコンポーネントを作る

ターミナルにてfrontend/modulesに移動したら、Header.jsxファイルを作成します。

import React from 'react';
import '../css/common.css';
import '../css/Header.css';

function Header(){
    return(
        <>
        <header className="Header">
            <ul>
                <li className="H_logo">
                    <p>Books</p>
                </li>
                <li>
                    aiueo
                </li>
            </ul>
        </header>
        </>
    );
}

export default Header;

 

次に、/frontend/cssディレクトリに移動して2つのCSSファイルを作成します。

common.css

/*setting*/
html,body,div,span,object,iframe,h1,h2,h3,h4,h5,h6,p,
blockquote,pre,abbr,address,cite,code,del,dfn,em,img,
ins,kbd,q,samp,small,strong,sub,sup,var,b,i,dl,dt,dd,
ol,ul,li,fieldset,form,label,legend,table,caption,
tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,
figcaption,figure,footer,header,hgroup,menu,nav,section,
summary,time,mark,audio,video {
    font-size: 100%;
    margin: 0;
    padding: 0;
    vertical-align: baseline;
    border: 0;
    outline: 0;
    background: transparent;
}
body {
    line-height: 1;
}
article,aside,details,figcaption,figure,
footer,header,hgroup,menu,nav,section {
    display: block;
}
nav ul {
    list-style: none;
}
blockquote,
q {
    quotes: none;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
    content: '';
    content: none;
}
a {
    font-size: 100%;
    margin: 0;
    padding: 0;
    vertical-align: baseline;
    background: transparent;
}
ins {
    text-decoration: none;
    color: #000;
    background-color: #ff9;
}
mark {
    font-weight: bold;
    font-style: italic;
    color: #000;
    background-color: #ff9;
}
del {
    text-decoration: line-through;
}
abbr[title],
dfn[title] {
    cursor: help;
    border-bottom: 1px dotted;
}
table {
    border-spacing: 0;
    border-collapse: collapse;
}
hr {
    display: block;
    height: 1px;
    margin: 1em 0;
    padding: 0;
    border: 0;
    border-top: 1px solid #ccc;
}
label{
	font-family:"Poppins";
	font-size:18px;
}
input{
	font-family:"Poppins";
	font-size:15px;
	color:#171717;
}
input{
	width:100%;
	height:40px;
	border-radius:3px;
	border:1px solid rgba(0, 0, 0, 0.1);
	background:#FCFCFC;
	margin:5px 0 0 0 ;
	padding:0 18px;
	box-sizing: border-box;
}
select{
	width:100%;
	height:40px;
	border-radius:3px;
	border:1px solid rgba(0, 0, 0, 0.1);
	background:#FCFCFC;
	display:inline-block;
    margin:5px 0 0 0 ;
	padding:0 18px;
	box-sizing: border-box;
	color:#161616;
	font-size:15px;
	appearance: none;
}
select::-ms-expand{
	display:none;
}
option{
	font-size:14px;
	font-family:"Poppins";
}
textarea{
	width:100%;
	height:200px;
	border-radius:3px;
	border:1px solid rgba(0, 0, 0, 0.1);
	background:#FCFCFC;
	margin:5px 0 0 0 ;
	padding:15px;
	box-sizing: border-box;
	color:#161616;
	font-family:"Poppins";
    font-size:15px;
}

/*setting more*/
body {
    font-family:"Noto Sans JP", fot-tsukubrdgothic-std, fot-tsukuardgothic-std, toppan-bunkyu-midashi-go-std, "Poppins","Yu Mincho", YuMincho, "YuMincho", "Yu Gothic", "YuGothic", "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Robot", serif;
    width:100%;
    background:#F2F2F2;

    margin:0px;
	overflow-x: hidden;
}
ul{
    padding-left:0px;
    list-style-type: none;
    margin:0;
}
p,a,h1,h2,h3,h4,div{
	font-size:16px;
	color:#000;
	text-decoration: none;
    font-family: "Hiragino Kaku Gothic ProN", "Hiragino Sans", "Noto Sans JP", "Yu Mincho", YuMincho, "YuMincho", "Yu Gothic", "YuGothic", "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Robot", serif;
    
}
p,a,li{
    letter-spacing: 0.5px;
	font-family: "Hiragino Kaku Gothic ProN", "Hiragino Sans", "Noto Sans JP", "Yu Mincho", YuMincho, "YuMincho", "Yu Gothic", "YuGothic", "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Robot", serif;
}

/*scrollbar*/
::-webkit-scrollbar{
    width: 15px;
}
::-webkit-scrollbar-track{
    background: #fff;
    border: none;
    border-radius: 10px;
    box-shadow: inset 0 0 0px #333333;
}
::-webkit-scrollbar-thumb{
    background: #efefef;
    border-radius: 10px;
    box-shadow: none;
}

.common_frame{
    width:100%;
    max-width:900px;
    margin:30px auto;
    padding:30px;
    box-sizing: border-box;
}
.common_frame form{
    margin:10px 0;
}
.common_frame input{
    margin:10px 0;
}

 

Header.css

header{
    width:100%;
    height:80px;
    background:#e0e0e0;
}
header>ul{
    width:100%;
    height:100%;
    padding:0 30px;
    box-sizing:border-box;
    display:flex;
    justify-content:space-between;
}
header>ul>li{
    height:100%;
    display:flex;
    align-items:center;
    font-weight:bold;
}
header>ul>li>a>p{
    font-size:16px;
    font-weight:bold;
}
header>ul>li>ul{
    display:flex;
    justify-content: flex-end;
}
header>ul>li>ul>li{
    margin: 0 0 0 15px;
}
header>ul>li>ul>li>a>p{
    font-size:16px;
    font-weight:bold;
}

 

最後に、frontend/srcディレクトリ直下のApp.jsファイルを次のように更新します。

import React, { useState,useEffect } from 'react';

//Headerコンポーネントを読み込む(拡張子は省略可)
//import Header from './modules/Header';これでもOK
import Header from './modules/Header.jsx';

function App() {
  return (
    //ReactのStrictモードでHeaderコンポーネントを表示
    <React.StrictMode>
      <Header/>
    </React.StrictMode>
  );
}

export default App;

 

ブラウザを確認すると、次のような画面が表示されているはずです。

React.StrictModeとは?
ReactのStrictモードとは、コードの安全性を保つための機能を有効にするためのラッパーです。
タグで囲まれたコードの中で非推奨のAPIなどが使われていると、その問題点を検知・警告してくれます。
UIの表示には関係ありませんが、使用しておいた方が無難でしょう。

Formコンポ―ネントを作る

同じ要領で、Formコンポーネントを作成します。

/frontend/src/modulesディレクトリに移動して、Form.jsxファイルを作成します。

import React, { useState } from 'react';
import '../css/common.css';

function Form(){
    return(
        <>
        <div className="Form common_frame">
            <p>ユーザー名</p>
            <input type="text" name=""></input>
            <input type="submit" name="" value="検索する" className="submit" />
        </div>
        </>
    );
}

export default Form;

 

/frontend/srcディレクトリのApp.jsファイルを変更して、Formコンポーネントを読み込み・表示します。

import React, { useState,useEffect } from 'react';
import Header from './modules/Header';
import Form from './modules/Form';

function App() {
  return (
    <React.StrictMode>
      <Header/>
      <Form/>
    </React.StrictMode>
  );
}

export default App;

 

ブラウザを確認すると、次のような画面が表示されているはずです。

Listコンポーネントを作る

同じ要領で、Listコンポーネントを作成します。

/frontend/src/modulesディレクトリに移動して、List.jsxファイルを作成します。

import React from 'react';
import '../css/common.css';
import '../css/List.css';

function List() {

    return (
        <>
        <div className="List common_frame">
            <ul className="item">
                <li>Day</li>
                <li>Title</li>
                <li>Content</li>
            </ul>
            <ul className="result">
                <li>12/1</li>
                <li>欲しいモノリスト</li>
                <li>きつね、たぬき、ねこ、スイカ</li>
            </ul>
        </div>
        </>
    );
}

export default List;

 

次に、/frontend/cssディレクトリに移動してList.cssファイルを作成します。

.List>ul{
    width:100%;
    height:40px;
    display:flex;
    flex-wrap:wrap;
}
.List>ul>li{
    display:flex;
    align-items: center;
    padding:0 10px;
    box-sizing: border-box;
}
.List>.result>li{
    background:#fff;
}
.List>ul>li:nth-child(1){
    width:10%;
}
.List>ul>li:nth-child(2){
    width:30%;
}
.List>ul>li:nth-child(3){
    width:60%;
}

 

/frontend/srcディレクトリのApp.jsファイルを変更して、Listコンポーネントを読み込み・表示します。

import React, { useState,useEffect } from 'react';
import Header from './modules/Header';
import Form from './modules/Form';
import List from './modules/List';

function App() {
  return (
    <React.StrictMode>
      <Header/>
      <Form/>
      <List/>
    </React.StrictMode>
  );
}

export default App;

 

以上でUIの作成は完了です。ブラウザを確認すると次のような画面が表示されるはずです。

コンポーネント間でデータを受け渡す方法

続いては、Reactのコンポーネント間でデータを受け渡す方法について解説します。コンポーネント間でデータ伝送を行うテクニックは、この後の「POST通信の実装」で活用します。

本章で行う作業はすべて、Reactの理解を深めるための実験ですので実際に試す必要はありません。(試してもいいけど、終わったらもとに戻すこと)

App.jsからForm.jsxへデータを渡す

App.jsファイルに、変数を保持するためのuseStateと初期値を設定します。

const [toform, setToform] = useState({
  test : "to Form.jsx"
});

 

次に、変数(useState)をFormコンポーネントに渡すため、return()内のコードを次のように変更します。

//変更前
<Form />

//変更後
<Form toform={toform} />

toformという新たな変数に、先ほど設定したuseStateの変数toformが代入されました。代入されたデータはそのままFormコンポ―ネントに渡されます。

 

次に、Form.jsxを開いてください。データの受け渡しにはpropsと呼ばれる仕組みを利用しているので、Form関数の引数にpropsを設定します。

//変更前
function Form(){

//変更後
function Form(props) {

 

Form関数内で定数testを定義し、props.toform.testを代入します。

const test = props.toform.test;
ちょっと一言
propsを利用して伝送したデータは、pops.変数名.オブジェクトkeyで取り出すことができます。
オブジェクトを利用していない場合は、props.変数名で取り出せます。

 

最後に、データを代入した定数testを表示します。return();内のinputタグの次に、以下のコードを追加してください。

<p>{test}</p>

 

ブラウザを確認すると、検索するボタンの下に「to form」と表示されるはずです。

上手くいかなかった人のために、一応App.jsとForm.jsxのコードを置いておきます。

App.js

import React, { useState,useEffect } from 'react';
import Header from './modules/Header';
import Form from './modules/Form';
import List from './modules/List';

function App() {
  const [toform, setToform] = useState({
    test:"to Form.jsx"
  });
  return (
    <React.StrictMode>
      <Header/>
      <Form toform={toform}/>
      <List/>
    </React.StrictMode>
  );
}

export default App;

 

Form.jsx

import React, { useState } from 'react';
import '../css/common.css';

function Form(props){
    const test = props.toform.test;
    return(
        <>
        <div className="Form common_frame">
            <p>ユーザー名</p>
            <input type="text" name=""></input>
            <input type="submit" name="" value="検索する" className="submit" />
            <p>{test}</p>
        </div>
        </>
    );
}

export default Form;

Form.jsxからApp.jsにデータを渡す

さっきと逆のことをします。

Form.jsxからApp.jsにデータを渡す場合も、必ずApp.js側でデータを保持するuseStateを設置しておく必要があります。先ほどの手順通り作業した人は、App.js側に既にuseStateが設置してあるはずですが、すでに初期値が設定してあるのでリセットします。

先ほど設置したApp.jsのuseStateの値を空白にしてください。

const [toform, setToform] = useState({
    test:""
  });

 

次に、FormコンポーネントにuseStateのsetToform変数を渡します。useStateのtoform変数は「データを渡すため」に利用しましたが、setToform変数は「データを受け取るため」に利用します。

<Form toform={toform} setToform={setToform}/>

 

最後に、Form.jsx側でも変数を保持するuseStateを設置します。useState変数を設置したら先ほど渡したsetToform変数に、toapp変数を代入します。

//useStateを定義
const [toapp, setToapp] = useState({
  test:"to App.js"
});

//setToform変数にデータを代入
props.setToform(toapp);

 

以上で作業完了です。ブラウザを確認すると、「to App.js」と表示されているはずです。

コンポーネント間のデータ伝送の仕組みは、なんとなくイメージできたでしょうか?

App.js

import React, { useState,useEffect } from 'react';
import Header from './modules/Header';
import Form from './modules/Form';
import List from './modules/List';

function App() {
  const [toform, setToform] = useState({
    test:""
  });
  return (
    <React.StrictMode>
      <Header/>
      <Form toform={toform} setToform={setToform}/>
      <List/>
    </React.StrictMode>
  );
}

export default App;

 

Form.jsx

import React, { useState } from 'react';
import '../css/common.css';

function Form(props){
    const test = props.toform.test;
    const [toapp, setToapp] = useState({
        test:"to App.js"
    });
    props.setToform(toapp);
    return(
        <>
        <div className="Form common_frame">
            <p>ユーザー名</p>
            <input type="text" name=""></input>
            <input type="submit" name="" value="検索する" className="submit" />
            <p>{test}</p>
        </div>
        </>
    );
}

export default Form;

POST通信の実装

本章では、検索フォームに入力したキーワードを使ってMySQLからデータを取得・表示できるようにしていきます。

 

 

コメントを残す

CAPTCHA