1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【備忘録】日本一わかりやすいReact-Redux講座 実践編 #12前半 「トランザクションを使って商品を注文しよう(前半)」

Posted at

はじめに

概要

この記事は、トラハック氏(@torahack_)が運営するYoutubeチャンネル『トラハックのエンジニア学習ゼミ【とらゼミ】』の『日本一わかりやすいReact-Redux講座 実践編』の学習備忘録です。

前回の講座で、ヘッダーのカートアイコンのバッチ数を、DB情報とリアルタイム同期させることができました。

今回は、カート商品の購入処理を実装する前段階として、カート詳細ページのビューファイルを作ります。

※ 前回記事: 【【備忘録】日本一わかりやすいReact-Redux講座 実践編 #11 「Firestoreでリアルタイムに表示を更新しよう」

動画URL

トランザクションを使って商品を注文しよう(前半)【日本一わかりやすいReact-Redux講座 実践編#12】

要点

  • リスト表示を実装する際は、mapメソッドでリストアイテム用コンポーネントをイテレートする。

完成系イメージ

12-1.gif

ヘッダーのカートアイコンをクリックすると、カート情報ページ( path: http://localhost:3000/cart)へ遷移します。

カートに入れた商品が表示されています。ゴミ箱アイコンを押すことで、カートから商品を削除できます。

メイン

**http://localhost:3000/cart**に対して、カート情報ページを作成します。CartList.jsxテンプレートに対してCartListItem.jsxを子コンポーネントとして配置します。

12-2.png

UIKitコンポーネントしてGreyBottun.jsxも追加します。

ファイル実装

実装ファイル
1.src/components/UIkit/GreyButton.jsx
2.src/components/UIkit/index.js
3.src/templates/CartList.jsx
4.src/templates/index.js
5.src/components/Products/CartListItem.jsx
6.src/components/Products/index.js
7.src/Router.jsx
1.src/components/UIkit/GreyButton.jsx
import React from "react";
import Button from "@material-ui/core/Button";
import {makeStyles} from "@material-ui/styles";

const useStyles = makeStyles((theme) => ({
  "button": {
    backgroundColor: theme.palette.grey["300"],
    fontSize: 16,
    height: 48,
    marginButton: 16,
    width: 256
  }
}))

const GreyButton = (props) => {

  const classes = useStyles();

  return(
    <Button className={classes.button} variant="contained" onClick={() => props.onClick()}>
      {props.label}
    </Button>
  )
}

export default GreyButton
2.src/components/UIkit/index.js
export {default as GreyButton} from "./GreyButton"
export {default as PrimaryButton} from "./PrimaryButton"
export {default as SelectBox} from "./SelectBox"
export {default as TextInput} from "./TextInput"

PrimaryButton.jsxの色を変えただけです。

3.src/templates/CartList.jsx
import React, { useCallback } from "react";
import {useDispatch, useSelector} from  "react-redux"
import List from "@material-ui/core/List"
import {makeStyles} from "@material-ui/core/styles";
import { getProductsInCart } from "../reducks/users/selectors";
import {CartListItem} from "../components/Products"
import {PrimaryButton, GreyButton} from "../components/UIkit"
import {push} from "connected-react-router"

const useStyles = makeStyles((theme) => ({
  root: {
    margin: '0 auto',
    maxWidth: 512,
    width: '100%'
  },
}));

const CartList = () => {
  const classes = useStyles();
  const dispatch = useDispatch();
  const selector = useSelector((state)=>state);
  const productsInCart = getProductsInCart(selector);

  const goToOrder = useCallback(() => {
    dispatch(push("/order/confirm"))
  },[])

  const backToTop = useCallback(() => {
    dispatch(push("/"))
  },[])

  return (
    <section className="c-section-wrapin">
      <h2 className="u-text__headline">ショッピングカート</h2>
      <List className={classes.root}>
        {productsInCart.length > 0 && (
            productsInCart.map(product => <CartListItem product={product} key={product.cartId} />)
        )}
      </List>
      <div className="module-spacer--medium" />
      <div className="p-grid__column">
        <PrimaryButton label={"レジへ進む"} onClick={goToOrder} />
        <div className="module-spacer--extra-extra-small"/>
        <GreyButton label={"ショッピングを続ける"} onClick={backToTop} />
      </div>
    </section>
  )
}

export default CartList

mapメソッドで、リストアイテムの子コンポーネントを繰り返し描画するのは、もうお馴染みですね。

4.src/templates/index.js
export {default as CartList} from './CartList' //追記
export {default as Home} from './Home'
export {default as ProductDetail} from './ProductDetail'
export {default as ProductEdit} from './ProductEdit'
export {default as ProductList} from './ProductList'
export {default as Reset} from './Reset'
export {default as SignIn} from './SignIn'
export {default as SignUp} from './SignUp'
5.src/components/Products/CartListItem.jsx
import React from 'react';
import Divider from '@material-ui/core/Divider';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
import {makeStyles} from "@material-ui/styles";
import DeleteIcon from "@material-ui/icons/Delete";
import IconButton from "@material-ui/core/IconButton";
import {useSelector} from "react-redux";
import {getUserId} from "../../reducks/users/selectors";
import {db} from "../../firebase/index"

const useStyles = makeStyles((theme)=>({
  list: {
    height: 128,
  },
  image: {
    objectFit: "cover",
    margin: 16,
    height: 96,
    width: 96
  },
  text: {
    width: "100%"
  }
}));

const CartListItem = (props) => {
  const classes = useStyles();
  const selector = useSelector(state => state);
  const uid = getUserId(selector)
  const image = props.product.images[0].path;
  const name = props.product.name
  const price = props.product.price.toLocaleString();
  const size = props.product.size

  const removeProductFromCart = (id) => {
    return  db.collection("users").doc(uid)
              .collection("cart").doc(id)
              .delete()
  }

  return (
    <>
      <ListItem className={classes.list}>
        <ListItemAvatar>
          <img className={classes.image} src={image} alt="商品画像"/>
        </ListItemAvatar>
        <div className={classes.text}>
          <ListItemText
            primary={name}
            secondary={"サイズ:" + size}
          />
          <ListItemText
            primary={"¥" + price}
          />
        </div>
        <IconButton onClick={() => removeProductFromCart(props.product.cartId)}>
          <DeleteIcon/>
        </IconButton>
      </ListItem>
      <Divider/>
    </>
  );

};

export default CartListItem

removeProductFromCart()で、カートの追加した商品をDB上から削除できるようにしています。

6.src/components/Products/index.js
export {default as CartListItem} from "./CartListItem" //追記
export {default as ImageArea} from "./ImageArea"
export {default as ImagePreview} from "./ImagePreview"
export {default as ImageSwiper} from "./ImageSwiper"
export {default as ProductCard} from "./ProductCard"
export {default as SetSizeArea} from "./SetSizeArea"
export {default as SizeTable} from "./SizeTable"
7.src/Router.jsx
import React from 'react';
import {Route, Switch} from "react-router";
import {CartList, ProductDetail,ProductEdit,ProductList,Reset,SignIn,SignUp} from "./templates";
import Auth from "./Auth"

const Router = () => {
  return (
    <Switch>
      <Route exact path={"/signup"} component={SignUp} />
      <Route exact path={"/signin"} component={SignIn} />
      <Route exact path={"/signin/reset"} component={Reset} />

      <Auth>
        <Route exact path={"(/)?"} component={ProductList} />
        <Route exact path={"/product/:id"} component={ProductDetail} />
        <Route path={"/product/edit(/:id)?"} component={ProductEdit} />
        <Route extct path={"/cart"} component={CartList} /> {*追記*}
      </Auth>
    </Switch>
  );
};

export default Router

"/cart"にルーティングします。

### 動作確認

全体の動作は「完成系イメージ」の通りです。

さいごに

今回の要点をおさらいすると、

  • リスト表示を実装する際は、mapメソッドでリストアイテム用コンポーネントをイテレートする。

以上です!次回、本格的に購入処理を実装していきます。

このような学習内容を日々呟いていますので、よろしければTwitter(@ddpmntcpbr)のフォローもよろしくお願いします。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?