MERN client行ってみよう
MERN client準備編
目次
- コンポーネントの雛形作成
- スタイル適応の準備
- reducer作成
前回の続きです。
今回はクライアントの処理を書いていきます。
今回作成していくもの
コンポーネントの雛形作成
ターミナルでclientに移動してパッケージをインストールします
% cd client % npm install @material-ui/core
次にclientフォルダー直下にcomponentsフォルダーを作成して
さらにその下にフォルダーを作っていきます。
Formフォルダーを作成し、その中にForm.jsファイルを作成
Postsフォルダーを作成し、その中に Posts.jsファイルを作成
PostsフォルダーにPostフォルダーを作成し、その中にPost.jsファイルを作成します。
次にAppコンポーネントを書いていきます。
client/src/App.js
import React from 'react'; // @material-ui/coreからコンポーネントをimport import { Container, AppBar, Typography, Grow, Grid } from '@material-ui/core' // 画像をimport import memories from './images/memories.jpg' const App = () => { return ( <Container maxWidth='lg'> {/* title部分 */} <AppBar position='static' color='inherit'> <Typography variant='h2' align='center' height='60'>memories</Typography> <img src={memories} alt='memories' height='60'/>{/* importした画像 */} </AppBar> {/* main部分 */} <Grow in> <Container> <Grid container justifyContent='space-between' alignItems='stretch' spacing={3}> {/* 投稿一覧が表示される部分 */} <Grid item xs={12} sm={7}> {/* 投稿一覧表示コンポーネント */} <Posts /> </Grid> {/* 投稿フォームが表示される部分 */} <Grid item xs={12} sm={4}> {/* 投稿フォーム表示コンポーネント */} <Form /> </Grid> </Grid> </Container> </Grow> </Container> ); }; export default App;
Formコンポーネントを追記
client/src/components/Form/Form.js
import React from 'react'; const Form = () => { return ( <h1> FORM </h1> ); }; export default Form;
Postsコンポーネントを追記
client/src/components/Posts/Posts.js
import React from 'react'; import Post from './Post/Post' const Posts = () => { return ( <> <h1>POSTS</h1> <Post/> <Post/> </> ); }; export default Posts;
Postコンポーネントを追記
client/src/components/Posts/Post/Post.js
import React from 'react'; const Post = () => { return ( <h1>POST</h1> ); }; export default Post;
client/src/App.jsのimport部分に下記のように追記します。
import React from 'react'; import Posts from './components/Posts/Posts'; //追記 import Form from './components/Form/Form'; //追記 // @material-ui/coreからコンポーネントをimport import { Container, AppBar, Typography, Grow, Grid } from '@material-ui/core' // 画像をimport import memories from './images/memories.jpg'
現状このように画面が表示されています。
スタイルは当ててないので画像は変な感じになってると思います。
スタイル適応の準備
スタイルを当てていきます。
まずは各フォルダーにstyles.jsというファイルを作っていきます。
client/src/styles.js
client/src/components/Form/styles.js
client/src/components/Posts/styles.js
client/src/components/Posts/Post/styles.js
次に各ファイルの中身を書いていきます。
client/src/styles.js
import { makeStyles } from "@material-ui/core/styles"; export default makeStyles((theme) => ({ appBar: { borderRadius: 15, margin: '30px 0', display: 'flex', flexDirection: 'row', justifyContent: 'center', alignItems: 'center', }, heading: { color: 'rgba(0,183,255, 1)', }, image: { marginLeft: '15px', }, [theme.breakpoints.down('sm')]: { mainContainer: { flexDirection: 'column-reverse' } }, }))
client/src/Form/styles.js
import { makeStyles } from '@material-ui/core/styles'; export default makeStyles((theme) => ({ root: { '& .MuiTextField-root': { margin: theme.spacing(1), }, }, paper: { padding: theme.spacing(2), }, form: { display: 'flex', flexWrap: 'wrap', justifyContent: 'center', }, fileInput: { width: '97%', margin: '10px 0', }, buttonSubmit: { marginBottom: 10, }, }));
client/src/Posts/styles.js
import { makeStyles } from '@material-ui/core/styles'; export default makeStyles((theme) => ({ mainContainer: { display: 'flex', alignItems: 'center', }, smMargin: { margin: theme.spacing(1), }, actionDiv: { textAlign: 'center', }, }));
client/src/Posts/Post/styles.js
import { makeStyles } from '@material-ui/core/styles'; export default makeStyles({ media: { height: 0, paddingTop: '56.25%', backgroundColor: 'rgba(0, 0, 0, 0.5)', backgroundBlendMode: 'darken', }, border: { border: 'solid', }, fullHeightCard: { height: '100%', }, card: { display: 'flex', flexDirection: 'column', justifyContent: 'space-between', borderRadius: '15px', height: '100%', position: 'relative', }, overlay: { position: 'absolute', top: '20px', left: '20px', color: 'white', }, overlay2: { position: 'absolute', top: '20px', right: '20px', color: 'white', }, grid: { display: 'flex', }, details: { display: 'flex', justifyContent: 'space-between', margin: '20px', }, title: { padding: '0 16px', }, cardActions: { padding: '0 16px 8px 16px', display: 'flex', justifyContent: 'space-between', }, });
それぞれのコンポーネントで呼び出して定義をしておきます。
client/src/App.js
import React, { useEffect } from 'react'; import Posts from './components/Posts/Posts'; //追記 import Form from './components/Form/Form'; //追記 // @material-ui/coreからコンポーネントをimport import { Container, AppBar, Typography, Grow, Grid } from '@material-ui/core' // 画像をimport import memories from './images/memories.jpg' import useStyles from './styles' //追記 const App = () => { const classes = useStyles() //追記 return ( <Container maxWidth='lg'> {/* title部分 */} <AppBar className={classes.appBar} position='static' color='inherit'> <Typography className={classes.heading} variant='h2' align='center' height='60'>memories</Typography> <img className={classes.image} src={memories} alt='memories' height='60'/>{/* importした画像 */} </AppBar> {/* main部分 */} <Grow in> <Container> <Grid container justifyContent='space-between' alignItems='stretch' spacing={3}> {/* 投稿一覧が表示される部分 */} <Grid item xs={12} sm={7}> {/* 投稿一覧表示コンポーネント */} <Posts /> </Grid> {/* 投稿フォームが表示される部分 */} <Grid item xs={12} sm={4}> {/* 投稿フォーム表示コンポーネント */} <Form /> </Grid> </Grid> </Container> </Grow> </Container> ); }; export default App;
client/src/components/Form/Form.js
import React from 'react'; import useStyles from './styles' //追記 const Form = () => { const classes = useStyles() //追記 return ( <h1> FORM </h1> ); }; export default Form;
client/src/components/Posts/Posts.js
import React from 'react'; import Post from './Post/Post' import useStyles from './styles' //追記 const Posts = () => { const classes = useStyles() //追記 return ( <> <h1>POSTS</h1> <Post/> <Post/> </> ); }; export default Posts;
client/src/components/Posts/Post/Post.js
import React from 'react'; import useStyles from './styles' const Post = () => { const classes = useStyles() return ( <h1>POST</h1> ); }; export default Post;
reducer作成
次にapiをまとめるフォルダーを作ります
client/src直下にapiというフォルダーを作成してindex.jsファイルを作成します。
同じくclient/src直下にaction, reducersというフォルダーを用意し、それぞれにposts.jsというファイルを用意します。reducersにはindex.jsファイルも用意します。
それではそれぞれの中身を書いていきます。
の前にreact-reduxをインストールします
% cd client % npm install react-redux
中身を書いていきます。
client/src/reducers/index.js
import { combineReducers } from "redux"; // client/src/reducers/postsをimport import posts from './posts' // combineReducersにpostsを登録する export default combineReducers({ posts, })
client/src/reducers/posts.js
// 即時関数をそのままexport default export default (posts = [], action) => { // state(今回の場合はposts)とactionを引数に取る switch (action.type) { case 'FETCH_ALL': return action.payload case 'CREATE': return posts default: return posts; } }
client/src/actions/posts.js
// client/apiの全てをimport import * as api from '../api' // Action Creators 実処理 export const getPosts = () => async (dispatch) => { // 非同期 try { // client/api/index.jsのfetchPosts関数を実行 const { data } = await api.fetchPosts() // actionにtypeとpayloadを渡す payloadには上で取得したdataを格納する dispatch({type: 'FETCH_ALL', payload: data}) } catch (error) { console.log(error.message) } }
client/src/index.js
import React from 'react' import ReactDOM from 'react-dom' import { Provider } from 'react-redux' //追記 import { createStore, applyMiddleware, compose } from 'redux' //追記 import thunk from 'redux-thunk' //追記 import reducers from './reducers' //追記 import App from './App' // client/reducers/からreducerをimportし、storeを作成する const store = createStore(reducers, compose(applyMiddleware(thunk))) ReactDOM.render( // ストアを使用する範囲をProviderで囲みstoreに上で作成したstoreを定義 <Provider store={store}> <App /> </Provider>, document.getElementById('root') )
client/src/App.jsも追記します
import React, { useEffect } from 'react'; import Posts from './components/Posts/Posts'; //追記 import Form from './components/Form/Form'; //追記 // @material-ui/coreからコンポーネントをimport import { Container, AppBar, Typography, Grow, Grid } from '@material-ui/core' // dispatch使用に使うhooks import { useDispatch } from 'react-redux' // client/src/actionsからgetPosts関数をimport import { getPosts } from './actions/posts' // 画像をimport import memories from './images/memories.jpg' import useStyles from './styles' const App = () => { const classes = useStyles() // dispatchを定義 const dispatch = useDispatch() useEffect(() => { // getPosts関数を実行 dispatch(getPosts()) },[dispatch]) return ( <Container maxWidth='lg'> {/* title部分 */} <AppBar className={classes.appBar} position='static' color='inherit'> <Typography className={classes.heading} variant='h2' align='center' height='60'>memories</Typography> <img className={classes.image} src={memories} alt='memories' height='60'/>{/* importした画像 */} </AppBar> {/* main部分 */} <Grow in> <Container> <Grid container justifyContent='space-between' alignItems='stretch' spacing={3}> {/* 投稿一覧が表示される部分 */} <Grid item xs={12} sm={7}> {/* 投稿一覧表示コンポーネント */} <Posts /> </Grid> {/* 投稿フォームが表示される部分 */} <Grid item xs={12} sm={4}> {/* 投稿フォーム表示コンポーネント */} <Form /> </Grid> </Grid> </Container> </Grow> </Container> ); }; export default App;
ややこしくなってきたので一旦図でまとめてみました。
間違ってたら教えてください・・・
長くなってきたので今回はここまでにします。
現在のclientのフォルダ構成はこんな感じになってると思います。
次回も引き続きクライアント側の表示/投稿処理を実装していきます。
今回はここまで
続きは次回!
それではまた!