React
今後のフロントエンドのデファクトスタンダードになるReact
日本国内ではVue.jsの勢いがあるが
今後はReactがくると思っているので自分はReact学びます。
誰もが使う技術を使うっていうのは非常に自分としては大事にしています。
- その技術が使える人がいることで保守運用できる。
- 採用する際にも人が集まる。
- 技術の情報が集まる
などなど非常にメリットが多いと思っています。
component志向の言語となるので
画面に必要なパーツを分散させて保守性をあげるという非常にメリットがあるものになります。
自分が新規で開発しているサービスで簡単に解説できたらなと思ってます。
import React, {createContext, useEffect, useState} from 'react';
import {BrowserRouter as Router, Link, Route, useParams} from 'react-router-dom';
// ComponentのImport
import Home from './components/home/Home'
import './App.css';
import TimeLine from "./components/home/timeLine";
import Requirement from "./components/requirement/Requirement";
import RequirementDetail from "./components/requirement-detail/RequirementDetail";
const requirementInfo =
[
{
'name': '石田',
'title': '新宿で焼肉会',
'place': '新宿'
},
{
'name': '石田',
'title': '新宿で焼肉会',
'place': '新宿'
},
{
'name': '石田',
'title': '新宿で焼肉会',
'place': '新宿'
}
]
export const RequirementContext = createContext(requirementInfo)
function App() {
const AppName = 'MeatMeet?'
// Hooks
const [requirement, setRequirement] = useState(requirementInfo)
console.log()
useEffect(() => {
console.log('hello hooks')
})
return (
<>
<div className="App">
<RequirementContext.Provider value={requirement}>
<Router>
<header className="App-header">
<h2>{AppName}</h2>
<Link to="/">Home</Link>
<Link to="/requirement">募集</Link>
</header>
<Route exact path="/" component={Home}/>
<Route exact path="/requirement" component={Requirement}/>
<Route exact path="/requirement/detail/:id" component={RequirementDetail}/>
</Router>
</RequirementContext.Provider>
</div>
</>
);
}
export default App;
公正としては
こんな感じになってます。
一番わかりやすいのがhomeだと思うのですが
- Home.tsx
- timeLine.tsx
- timeLineModal.tsx
これに加えて全ての画面で共通して使う場所として
App.tsx
もあります。
の三つのコンポーネントが合わさって初めて
Home画面ができるというイメージです。
import React, {createContext, useEffect, useState} from 'react';
import {BrowserRouter as Router, Link, Route, useParams} from 'react-router-dom';
// ComponentのImport
import Home from './components/home/Home'
import './App.css';
import TimeLine from "./components/home/timeLine";
import Requirement from "./components/requirement/Requirement";
import RequirementDetail from "./components/requirement-detail/RequirementDetail";
const requirementInfo =
[
{
'name': '石田',
'title': '新宿で焼肉会',
'place': '新宿'
},
{
'name': '石田',
'title': '新宿で焼肉会',
'place': '新宿'
},
{
'name': '石田',
'title': '新宿で焼肉会',
'place': '新宿'
}
]
export const RequirementContext = createContext(requirementInfo)
function App() {
const AppName = 'MeatMeet?'
// Hooks
const [requirement, setRequirement] = useState(requirementInfo)
console.log()
useEffect(() => {
console.log('hello hooks')
})
return (
<>
<div className="App">
<RequirementContext.Provider value={requirement}>
<Router>
<header className="App-header">
<h2>{AppName}</h2>
<Link to="/">Home</Link>
<Link to="/requirement">募集</Link>
</header>
<Route exact path="/" component={Home}/>
<Route exact path="/requirement" component={Requirement}/>
<Route exact path="/requirement/detail/:id" component={RequirementDetail}/>
</Router>
</RequirementContext.Provider>
</div>
</>
);
}
export default App;
Homeコンポーネント
import React, {useEffect, useState, createContext} from 'react';
import Button from '@material-ui/core/Button';
import EventIcon from '@material-ui/icons/Event';
import {BrowserRouter as Router, Link} from "react-router-dom";
import TimeLine from './timeLine'
const requirementInfo =
[
{
'name': '石田',
'title': '新宿で焼肉会',
'place': '新宿'
},
{
'name': '石田',
'title': '新宿で焼肉会',
'place': '新宿'
},
{
'name': '石田',
'title': '新宿で焼肉会',
'place': '新宿'
}
]
export const HomeContext = createContext(requirementInfo)
function Home() {
const [requirement, setRequirement] = useState(requirementInfo)
return (
<HomeContext.Provider value={{...requirement, ...requirementInfo}}>
<div className="home">
<img src="" alt="homeLogo"/>
<Button
to="/requirement"
component={Link}
variant="contained"
color="secondary"
startIcon={<EventIcon/>}
className="requirement">募集する
</Button>
<TimeLine/>
</div>
</HomeContext.Provider>
);
}
export default Home;
タイムライン部分
import React, {useContext, useEffect} from "react";
import Modal from '@material-ui/core/Modal';
import Button from '@material-ui/core/Button';
import makeStyles from "@material-ui/core/styles/makeStyles";
import {BrowserRouter as Router, Route, Link, Switch, BrowserRouter} from "react-router-dom";
import {withRouter} from 'react-router-dom';
import { useHistory } from 'react-router';
import {HomeContext} from './Home'
import Requirement from "../requirement/Requirement";
import RequirementDetail from "../requirement-detail/RequirementDetail";
function TimeLine(this: any) {
const requirementInfo = useContext(HomeContext)
let info = Object.entries(requirementInfo).map(([key, value]) => ({key, value}))
console.log(info)
info.map(info => {
console.log(info)
})
const history = useHistory();
const handleOnclickDetail = (number: number) => (events: any) =>{
history.push('/requirement/detail/'+number,)
}
return (
<div className="timeLine">
<h3>タイムライン</h3>
<Router>
{info.map((info,index) => {
const resetIndex = index+1
return (
<div className="timeLine__requirement">
<img src="" alt="userLogo"/>
<div>{resetIndex}</div>
<div className="timeLine__name">イベントNo.{info.value.name}</div>
<div className="timeLine__content">{info.value.title}</div>
<div className="timeLine__area">{info.value.place}</div>
<Button onClick={handleOnclickDetail(resetIndex)}>{resetIndex}へ</Button>
</div>
)
})}
</Router>
</div>
)
}
export default withRouter(TimeLine)
タイムラインモーダルコンポーネント
import React from 'react';
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles';
import Modal from '@material-ui/core/Modal';
function rand() {
return Math.round(Math.random() * 20) - 10;
}
function getModalStyle() {
const top = 50 + rand();
const left = 50 + rand();
return {
top: `${top}%`,
left: `${left}%`,
transform: `translate(-${top}%, -${left}%)`,
};
}
const useStyles = makeStyles((theme: Theme) =>
createStyles({
paper: {
position: 'absolute',
width: 400,
backgroundColor: theme.palette.background.paper,
border: '2px solid #000',
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
}),
);
function timeLineModal() {
const classes = useStyles();
// getModalStyle is not a pure function, we roll the style only on the first render
const [modalStyle] = React.useState(getModalStyle);
const [open, setOpen] = React.useState(false);
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const body = (
<div style={modalStyle} className={classes.paper}>
<h2 id="simple-modal-title">Text in a modal</h2>
<p id="simple-modal-description">
Duis mollis, est non commodo luctus, nisi erat porttitor ligula.
</p>
</div>
);
return (
<div>
<button type="button" onClick={handleOpen}>
Open Modal
</button>
<Modal
open={open}
onClose={handleClose}
aria-labelledby="simple-modal-title"
aria-describedby="simple-modal-description"
>
{body}
</Modal>
</div>
);
}
export default timeLineModal
###hooks
従来のReactでは状態管理といえばReduxしかなかったため
それが使われていたイメージです。
一方で今日ではhooksという技術も使われています。
Reduxに関しては死ぬほどAngular内で学ぶことになりそうなので一旦Hooksの理解に努めます。
Hooksは基本的に親のComponentで定義した値をその
子供のComponentの中でも使えるようにする技術だと自分は理解しています。
これを使うことで
全てのComponentで同じ値を用いることができるというのが利点ですよね。
それぞれのComponent
で異なる値を用いていた場合、非常に面倒な処理が待っているのが地獄なので
状態管理という手法が今日では用いられています。
hooksの中でも
- useState
- useEffect
- useContext
の3つを簡単にまとめられたらと思っています
import React, {createContext, useEffect, useState} from 'react';
import {BrowserRouter as Router, Link, Route, useParams} from 'react-router-dom';
// ComponentのImport
import Home from './components/home/Home'
import './App.css';
import TimeLine from "./components/home/timeLine";
import Requirement from "./components/requirement/Requirement";
import RequirementDetail from "./components/requirement-detail/RequirementDetail";
const requirementInfo =
[
{
'name': '石田',
'title': '新宿で焼肉会',
'place': '新宿'
},
{
'name': '石田',
'title': '新宿で焼肉会',
'place': '新宿'
},
{
'name': '石田',
'title': '新宿で焼肉会',
'place': '新宿'
}
]
export const RequirementContext = createContext(requirementInfo)
function App() {
const AppName = 'MeatMeet?'
// Hooks
const [requirement, setRequirement] = useState(requirementInfo)
console.log()
useEffect(() => {
console.log('hello hooks')
})
return (
<>
<div className="App">
<RequirementContext.Provider value={requirement}>
<Router>
<header className="App-header">
<h2>{AppName}</h2>
<Link to="/">Home</Link>
<Link to="/requirement">募集</Link>
</header>
<Route exact path="/" component={Home}/>
<Route exact path="/requirement" component={Requirement}/>
<Route exact path="/requirement/detail/:id" component={RequirementDetail}/>
</Router>
</RequirementContext.Provider>
</div>
</>
);
}
export default App;
###useStateを使う
const [count, setCount] = useState(0)
第一引数にパラメータが入ってきます。
第二引数は第一引数にあるパラメータを変化させる時に用います。
また右辺のuseStateの引数には初期値requirementに入れたい初期値が入ってきます。
この場合はcount=0が入ってきます。
<button onClick={():void => setCount(count => count+1)}>+1</button>
そして上記のように書いてあげることでボタンをおすびにcountが+1されるような処理が行われます。
(void属性で帰ってくるように指定してあげないとtsxではエラーが表示されます。。。)
これで少しTypeScriptについて詳しくなった気がします。
###useEffectを使う
useEffectはレンダーの結果が画面に反映された後に表示されます。
useEffect(() => {
//レンダーの結果が画面に表示された後表示される
console.log('effect');
}
import React, {createContext, useEffect, useState} from 'react';
import {BrowserRouter as Router, Link, Route, useParams} from 'react-router-dom';
// ComponentのImport
import Home from './components/home/Home'
import './App.css';
import TimeLine from "./components/home/timeLine";
import Requirement from "./components/requirement/Requirement";
import RequirementDetail from "./components/requirement-detail/RequirementDetail";
const requirementInfo =
[
{
'name': '石田',
'title': '新宿で焼肉会',
'place': '新宿'
},
{
'name': '石田',
'title': '新宿で焼肉会',
'place': '新宿'
},
{
'name': '石田',
'title': '新宿で焼肉会',
'place': '新宿'
}
]
export const RequirementContext = createContext(requirementInfo)
function App() {
const AppName = 'MeatMeet?'
// Hooks
const [requirement, setRequirement] = useState(requirementInfo)
const [count, setCount] = useState(0)
console.log('render start')
useEffect(() => {
console.log('render finished')
})
})
...
というファイルの場合
render start
render finishedの順に描画されていることがわかるかと思います。
APIで画面描画後に何かしら処理したい
時などに用いられます。
また第二引数にcontextを指定して指定したparamが変わった場合にだけ
effectを実行するという指定もできます。
全ての変更で全く違う処理が走ると大変ですからね。
###createContextを使う
createContext
export const RequirementContext = createContext(requirementInfo)
createContextで定義することによってcontextが生成されます。
contextとは言うなればglobalに使えるstateという認識になります。
親で定義した共通の値を子コンポーネントで用いるためにまず準備してあげます。
###useContextを使う
import React, {useEffect, useState, createContext, useContext} from 'react';
import Button from '@material-ui/core/Button';
import EventIcon from '@material-ui/icons/Event';
import TextField from '@material-ui/core/TextField';
import {
Route,
Switch,
useParams,
useHistory,
useLocation,
} from 'react-router-dom';
import {RequirementContext} from "../../App";
function RequirementDetail(this: any) {
const urlParam:any = useParams();
const requirementId = urlParam.id
const requirements = useContext(RequirementContext)
const requirementDetailInfo = requirements[requirementId-1]
return (
<>
<div className="requirementDetail">
<div className="requirementDetail__timeline">
TimeLine
</div>
<div>
<div className="requirementDetail__detail">
募集詳細:
ID: {requirementId}
タイトル: {requirementDetailInfo.title}
募集者: {requirementDetailInfo.name}
場所: {requirementDetailInfo.place}
</div>
<div className="requirementDetail__talk">
やりとり
</div>
</div>
</div>
</>
);
}
export default RequirementDetail;
const requirements = useContext(RequirementContext)
これが肝になります。
親componentからimportしてきてた RequirementContext
をここで用いるというのを宣言してあげています。
またobjectのまま画面に表示させ量とすると
Objects are not valid as a React child (found: object with keys {name, title, place}). If you meant to render a collection of children, use an array instead.
というobjectはreactでは表示できないというエラーが発生するので展開してあげる必要があります。
今回は詳細画面なので必要なものだけ処理していますが
リストのように一覧表示させたいときはmapやスプレッド構文などで対応してあげる必要があります。
簡単にまとめてみましたが
まだまだ自分も使いこなせるわけではないのでこれからも精進していきます。