javascript - React 多重动态路由

标签 javascript reactjs react-router

我正在制作一个产品展示应用,它需要在不同级别的分类和产品之间进行动态路由。

我想出的问题是:

enter image description here

图片的前半部分(卡片)是 ProductList 组件。下半部分是 Product 组件。

import React, { Component } from "react";
import { BrowserRouter, NavLink, Route } from "react-router-dom";
import Card from "@material-ui/core/Card";
import CardActionArea from "@material-ui/core/CardActionArea";
import CardContent from "@material-ui/core/CardContent";
import CardMedia from "@material-ui/core/CardMedia";
import Typography from "@material-ui/core/Typography";
import equal from "fast-deep-equal";
import "./ProductList.scss";
import Product from "./Product";

const products = [
  {
    barcode: "8434786605357",
    collection: "2019",
    colorCode: "0",
    colorName: "DENIM",
    familyName: "Denim Pants",
    genericProduct: "PM200338WV7",
    id: 263934,
    image: [
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PM200338WV7/PM200338WV7_000_01_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PM200338WV7/PM200338WV7_000_02_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PM200338WV7/PM200338WV7_000_03_MO.jpg"
    ],
    line: "Men",
    productCode: "PM200338WV7000040",
    productName: "FINSBURY SKINNY FIT LOW WAIST JEANS",
    productNameLang: "en",
    season: "AW",
    size: "40",
    subfamilyName: "Jeans"
  },
  {
    barcode: ["8434786289854", "8434786289847", "8434786289861"],
    collection: "2019",
    colorCode: "0AA",
    colorName: "MULTI",
    familyName: "Dresses",
    genericProduct: "PL952477",
    id: [263935, 263938, 263939],
    image: [
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PL952477/PL952477_0AA_01_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PL952477/PL952477_0AA_02_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PL952477/PL952477_0AA_03_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PL952477/PL952477_0AA_01_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PL952477/PL952477_0AA_02_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PL952477/PL952477_0AA_03_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PL952477/PL952477_0AA_01_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PL952477/PL952477_0AA_02_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PL952477/PL952477_0AA_03_MO.jpg"
    ],
    line: "Women",
    productCode: ["PL9524770AAM", "PL9524770AAL", "PL9524770AAS"],
    productName: "FIONA CROSSOVER DRESS",
    productNameLang: "en",
    season: "SS",
    size: ["M", "L", "S"],
    subfamilyName: "Dresses NS/SS"
  },
  {
    barcode: "8434786258508",
    collection: "2019",
    colorCode: "599",
    colorName: "NACHT",
    familyName: "Swimwear",
    genericProduct: "PMB10211",
    id: 263936,
    image: [
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PMB10211/PMB10211_599_01_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PMB10211/PMB10211_599_02_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PMB10211/PMB10211_599_03_MO.jpg"
    ],
    line: "Men",
    productCode: "PMB10211599XL",
    productName: "ALMONTE SHORT SWIMSUIT",
    productNameLang: "en",
    season: "SS",
    size: "XL",
    subfamilyName: "Swimsuits"
  }
];

const productCategories = [
  {
    grandparent: null,
    level: 1,
    name: "Men",
    parent: null,
    path: "/men"
  },
  {
    grandparent: null,
    level: 2,
    name: "Denim Pants",
    parent: "Men",
    path: "/men/denim-pants"
  },
  {
    grandparent: "Men",
    level: 3,
    name: "Jeans",
    parent: "Denim Pants",
    path: "/men/denim-pants/jeans"
  },
  {
    grandparent: null,
    level: 1,
    name: "Women",
    parent: null,
    path: "/women"
  },
  {
    grandparent: null,
    level: 2,
    name: "Dresses",
    parent: "Women",
    path: "/women/dresses"
  },
  {
    grandparent: "Women",
    level: 3,
    name: "Dresses NS/SS",
    parent: "Dresses",
    path: "/women/dresses/dresses-ns-ss"
  },
  {
    grandparent: null,
    level: 2,
    name: "Swimwear",
    parent: "Men",
    path: "/men/swimwear"
  },
  {
    grandparent: "Men",
    level: 3,
    name: "Swimsuits",
    parent: "Swimwear",
    path: "/men/swimwear/swimsuits"
  },
  {
    grandparent: null,
    level: 2,
    name: "T-Shirts",
    parent: "Men",
    path: "/men/t-shirts"
  },
  {
    grandparent: "Men",
    level: 3,
    name: "SS T-Shirts",
    parent: "T-Shirts",
    path: "/men/t-shirts/ss-t-shirts"
  },
  {
    name: "T-Shirts",
    path: "/women/t-shirts",
    parent: "Women",
    grandparent: null,
    level: 2
  },
  {
    grandparent: "Women",
    level: 3,
    name: "SS T-Shirts",
    parent: "T-Shirts",
    path: "/women/t-shirts/ss-t-shirts"
  }
];

class ProductList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      filteredProducts: []
    };
  }

  componentDidUpdate(prevProps) {
    if (!equal(this.props, prevProps)) {
      this.filterProducts();
    }
  }

  filterProducts() {
    let productsFiltered = null;
    console.log(products);

    if (this.props.match.params.subfamily !== undefined) {
      productsFiltered = products
        .filter(
          product =>
            product.line
              .toLowerCase()
              .replace(/\s+/g, "-")
              .replace(/\//g, "-") === this.props.match.params.line
        )
        .filter(
          product =>
            product.familyName
              .toLowerCase()
              .replace(/\s+/g, "-")
              .replace(/\//g, "-") === this.props.match.params.family
        )
        .filter(
          product =>
            product.subfamilyName
              .toLowerCase()
              .replace(/\s+/g, "-")
              .replace(/\//g, "-") === this.props.match.params.subfamily
        );
    } else if (this.props.match.params.family !== undefined) {
      productsFiltered = products
        .filter(
          product =>
            product.line
              .toLowerCase()
              .replace(/\s+/g, "-")
              .replace(/\//g, "-") === this.props.match.params.line
        )
        .filter(
          product =>
            product.familyName
              .toLowerCase()
              .replace(/\s+/g, "-")
              .replace(/\//g, "-") === this.props.match.params.family
        );
    } else {
      productsFiltered = products.filter(
        product =>
          product.line
            .toLowerCase()
            .replace(/\s+/g, "-")
            .replace(/\//g, "-") === this.props.match.params.line
      );
    }

    this.setState({
      filteredProducts: productsFiltered
    });
  }

  render() {
    return (
      <BrowserRouter>
        <div className="product-cards">
          {this.state.filteredProducts.map(product => (
            <Card
              key={product.id}
              className={product.productName + " card"}
              style={{ maxWidth: 145, margin: 10 }}
            >
              <CardActionArea>
                <NavLink to={`/${product.id}`}>
                  <CardMedia
                    component="img"
                    alt="Product Image"
                    height="240"
                    image={product.image[0]}
                    title={product.productName}
                  />
                  <CardContent>
                    <Typography
                      gutterBottom
                      variant="h5"
                      component="p"
                      style={{ fontSize: ".8rem" }}
                    >
                      {product.productName}
                    </Typography>
                  </CardContent>
                </NavLink>
              </CardActionArea>
            </Card>
          ))}
        </div>
        <Route exact path={`/:product`} component={Product} />
      </BrowserRouter>
    );
  }
}

export default ProductList;
import React, { Component } from "react";
import equal from "fast-deep-equal";
import ImageGallery from "react-image-gallery";
import MenuItem from "@material-ui/core/MenuItem";
import FormControl from "@material-ui/core/FormControl";
import Select from "@material-ui/core/Select";
import "./Product.scss";

const productList = [
  {
    barcode: "8434786605357",
    collection: "2019",
    colorCode: "0",
    colorName: "DENIM",
    familyName: "Denim Pants",
    genericProduct: "PM200338WV7",
    id: 263934,
    image: [
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PM200338WV7/PM200338WV7_000_01_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PM200338WV7/PM200338WV7_000_02_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PM200338WV7/PM200338WV7_000_03_MO.jpg"
    ],
    line: "Men",
    productCode: "PM200338WV7000040",
    productName: "FINSBURY SKINNY FIT LOW WAIST JEANS",
    productNameLang: "en",
    season: "AW",
    size: "40",
    subfamilyName: "Jeans"
  },
  {
    barcode: ["8434786289854", "8434786289847", "8434786289861"],
    collection: "2019",
    colorCode: "0AA",
    colorName: "MULTI",
    familyName: "Dresses",
    genericProduct: "PL952477",
    id: [263935, 263938, 263939],
    image: [
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PL952477/PL952477_0AA_01_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PL952477/PL952477_0AA_02_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PL952477/PL952477_0AA_03_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PL952477/PL952477_0AA_01_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PL952477/PL952477_0AA_02_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PL952477/PL952477_0AA_03_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PL952477/PL952477_0AA_01_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PL952477/PL952477_0AA_02_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PL952477/PL952477_0AA_03_MO.jpg"
    ],
    line: "Women",
    productCode: ["PL9524770AAM", "PL9524770AAL", "PL9524770AAS"],
    productName: "FIONA CROSSOVER DRESS",
    productNameLang: "en",
    season: "SS",
    size: ["M", "L", "S"],
    subfamilyName: "Dresses NS/SS"
  },
  {
    barcode: "8434786258508",
    collection: "2019",
    colorCode: "599",
    colorName: "NACHT",
    familyName: "Swimwear",
    genericProduct: "PMB10211",
    id: 263936,
    image: [
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PMB10211/PMB10211_599_01_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PMB10211/PMB10211_599_02_MO.jpg",
      "http://tools.bexfyinfra.com/bexfy-touch-app-test/PMB10211/PMB10211_599_03_MO.jpg"
    ],
    line: "Men",
    productCode: "PMB10211599XL",
    productName: "ALMONTE SHORT SWIMSUIT",
    productNameLang: "en",
    season: "SS",
    size: "XL",
    subfamilyName: "Swimsuits"
  }
];

class Product extends Component {
  constructor(props) {
    super(props);
    this.state = {
      product: null,
      images: [
        {
          original: "https://picsum.photos/id/1018/1000/600/",
          thumbnail: "https://picsum.photos/id/1018/250/150/"
        },
        {
          original: "https://picsum.photos/id/1015/1000/600/",
          thumbnail: "https://picsum.photos/id/1015/250/150/"
        },
        {
          original: "https://picsum.photos/id/1019/1000/600/",
          thumbnail: "https://picsum.photos/id/1019/250/150/"
        }
      ]
    };
  }

  componentDidUpdate(prevProps) {
    if (!equal(this.props, prevProps)) {
      this.filterProduct();
    }
  }

  filterProduct() {
    let productId = this.props.match.params.product;
    console.log(productId);
    let selectedProduct = productList.filter(
      product => product.id == productId
    );
    console.log(selectedProduct);
    let images = selectedProduct[0].image.map(img => {
      return { original: img, thumbnail: img };
    });
    console.log(productId);
    console.log(this.state);
    this.setState({
      product: selectedProduct,
      images: images
    });
  }

  handleChangeSize = event => {
    this.setState({ selectedSize: event.target.value });
  };

  handleChangeColor = event => {
    this.setState({ selectedColor: event.target.value });
  };

  render() {
    return (
      <div>
        {this.state.product !== null && (
          <>
            <ImageGallery items={this.state.images} thumbnailPosition="left" />
            <div className="product-description">
              <h3>{this.state.product[0].productName}</h3>
              <h5>
                {Array.isArray(this.state.product[0].productCode)
                  ? this.state.product[0].productCode[0]
                  : this.state.product[0].productCode}
              </h5>
              {Array.isArray(this.state.product[0].size) ? (
                <form
                  styles={{
                    display: "flex",
                    flexWrap: "wrap"
                  }}
                >
                  <FormControl styles={{ minWidth: 120 }}>
                    <span className="product-label">Size:</span>
                    <Select
                      value={this.state.selectedSize}
                      onChange={this.handleChangeSize}
                    >
                      {this.state.product[0].size.map((item, index) => {
                        return (
                          <MenuItem
                            key={index}
                            value={this.state.product[0].size[index]}
                          >
                            {this.state.product[0].size[index]}
                          </MenuItem>
                        );
                      })}
                    </Select>
                  </FormControl>
                </form>
              ) : (
                <div>
                  <span className="product-label">Size:</span>
                  {this.state.product[0].size}
                </div>
              )}
              {Array.isArray(this.state.product[0].colorCode) ? (
                <form
                  styles={{
                    display: "flex",
                    flexWrap: "wrap"
                  }}
                >
                  <FormControl styles={{ minWidth: 120 }}>
                    <span className="product-label">Color:</span>
                    <Select
                      value={this.state.selectedColor}
                      onChange={this.handleChangeColor}
                    >
                      {this.state.product[0].colorCode.map((item, index) => {
                        return (
                          <MenuItem
                            key={index}
                            value={this.state.product[0].colorCode[index]}
                          >
                            {this.state.product[0].colorName[index]}
                          </MenuItem>
                        );
                      })}
                    </Select>
                  </FormControl>
                </form>
              ) : (
                <div>
                  <span className="product-label">Color:</span>
                  {this.state.product[0].colorName}
                </div>
              )}
            </div>
          </>
        )}
      </div>
    );
  }
}

export default Product;

我已经阅读了 Router 文档,但我通常不使用复杂的动态参数。我本来可以用路线的“精确” Prop 解决类似的问题,但它没有像动态生成的那样工作。

当我在产品 View 中时,我希望 ProductList 组件不显示,反之亦然。有办法吗?

实现此目标的最佳方法是什么?

CodeSandbox Playground :https://codesandbox.io/s/musing-firefly-tm9r0?fontsize=14

最佳答案

I have read the Router documentation but I don't usually use complex dynamic parameters.

我建议只提供产品id作为路由的参数。通常不需要更复杂的路线。所有其他产品数据(包括图片 URL)都应以状态存储。

I would want for the ProductList component to not show up when I'm in the Product View, and viceversa. Is there a way to do that?

是的,您应该将产品列表与路由器分开。应该有两个单独的类。呈现产品列表的一个。还有一种是注册路线并在产品列表和产品详细信息之间进行选择。具体来说,您应该使用 <Switch/>来自 react-router 的组件.

关于javascript - React 多重动态路由,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58084170/

相关文章:

javascript - 如何将此类基础高阶组件更改为功能组件?

javascript - 如何使用 react-router-dom 将 NavLink 设置为事件状态

react-router - 将路由器路径 react 为数组

reactjs - 使用 render() 时 REACT 路由器无法访问参数

javascript - 动态创建的 jQuery 动画 div

javascript - 将数组的索引插入一个对象

javascript - AngularJS JavaScript for 循环警告最多运行一次

reactjs - 如何在 Create React App Proxy 设置中使用 ENV 变量注入(inject)端口和主机?

javascript - 一个下拉字段会导致另一下拉字段的值更新

javascript - 为 Cypress 创建静态内容选择器