React Hooks 在路由之间不起作用,但在刷新时起作用

React Hooks doesnt work between routes but works when refreshing

我做了一个典型的电子商务,你点击产品然后会得到关于它的特定详细信息页面。 我想查看详细信息页面,所以我点击了产品,但它给了我未定义的错误,但是当我刷新页面时,它 returns 我想要的,只是当我用路线更改它时出现问题。

Cannot read 属性 'image' of undefined ,这意味着产品未定义, ProductState.js(我的背景)

import React, { useReducer } from "react";
import { productReducer } from "./productReducer";
import axios from "axios";
import {
  PRODUCT_DETAILS_FAIL,
  PRODUCT_DETAILS_REQUEST,
  PRODUCT_DETAILS_SUCCESS,
  PRODUCT_LIST_FAIL,
  PRODUCT_LIST_REQUEST,
  PRODUCT_LIST_SUCCESS,
} from "../constants/productConstants";

export const ProductContext = React.createContext();

const ProductState = ({ children }) => {
  const initialState = { products: [], product: { reviews: [] } };
  const [state, dispatch] = useReducer(productReducer, initialState);

  const listProductDetails = async (id) => {
    try {
      dispatch({ type: PRODUCT_DETAILS_REQUEST });
      const { data } = await axios.get(`/api/products/${id}`);
      dispatch({ type: PRODUCT_DETAILS_SUCCESS, payload: data });
    } catch (error) {
      dispatch({
        type: PRODUCT_DETAILS_FAIL,
        payload:
          error.response && error.response.data.message
            ? error.response.data.message
            : error.message,
      });
    }
  };
  return (
    <ProductContext.Provider
      value={{
        products: state.products,
        error: state.error,
        loading: state.loading,
        product: state.product,
        listProductDetails,
      }}
    >
      {children}
    </ProductContext.Provider>
  );
};

export default ProductState;

Productscreen.js(我想在哪看详细的具体产品)

import React, { useEffect, useContext, useState } from "react";
import { Link } from "react-router-dom";
import {Button,  Card,  Col,  Form,  Image,ListGroup,  Row,} from "react-bootstrap";
import { ProductContext } from "../productContext/productState";
import Spinnerr from "../components/Spinnerr";
import Message from "../components/Message";
const Productscreen = ({ match }) => {
  const context = useContext(ProductContext);
  const { listProductDetails, loading, error, product } = context;
  const [quantity, setQuantity] = useState(1);
  useEffect(() => {
    listProductDetails(match.params.id);
  }, []);

  return (
    <>
     
      <Link className="btn btn-light my-3" to="/">
        Back
      </Link>
      {loading ? (
        <Spinnerr />
      ) : error ? (
        <Message variant="danger">{error}</Message>
      ) : (
        <Row>
          <Col md={6}>
            <Image src={product.image} alt={product.name} fluid />
          </Col>
          <Col md={3}>
            <ListGroup variant="flush">
              <ListGroup.Item>
                <h3>{product.name}</h3>
              </ListGroup.Item>
              <ListGroup.Item>
                <Rating
                  rating={product.rating}
                  text={`${product.numReviews} reviews`}
                />
              </ListGroup.Item>
              <ListGroup.Item>Price: ${product.price}</ListGroup.Item>
              <ListGroup.Item>
                Description: {product.description}
              </ListGroup.Item>
            </ListGroup>
          </Col>
          <Col md={3}>
            <Card>
              <ListGroup variant="flush">
                <ListGroup.Item>
                  <Row>
                    <Col>Price:</Col>
                    <Col>
                      <strong>${product.price}</strong>
                    </Col>
                  </Row>
                </ListGroup.Item>
                <ListGroup.Item>
                  <Row>
                    <Col>Status:</Col>
                    <Col>
                      {product.countInStock > 0 ? "In Stock" : "Out of Stock"}
                    </Col>
                  </Row>
                </ListGroup.Item>
                {product.countInStock > 0 && (
                  <ListGroup.Item>
                    <Row>
                      <Col>Qty</Col>
                      <Col>
                        <Form.Control
                          as="select"
                          value={quantity}
                          onChange={(e) => setQuantity(e.target.value)}
                        >
                          {[...Array(product.countInStock).keys()].map((x) => {
                            return (
                              <option key={x + 1} value={x + 1}>
                                {x + 1}
                              </option>
                            );
                          })}
                        </Form.Control>
                      </Col>
                    </Row>
                  </ListGroup.Item>
                )}
                <ListGroup.Item>
                  <Button
                    className="btn-block"
                    type="button"
                    disabled={product.countInStock === 0}
                  >
                    Add to Cart
                  </Button>
                </ListGroup.Item>
              </ListGroup>
            </Card>
          </Col>
        </Row>
      )}
    </>
  );
};

export default Productscreen;

ProductReducer.js

import {
  PRODUCT_DETAILS_FAIL,
  PRODUCT_DETAILS_REQUEST,
  PRODUCT_DETAILS_SUCCESS,
  PRODUCT_LIST_FAIL,
  PRODUCT_LIST_REQUEST,
  PRODUCT_LIST_SUCCESS,
} from "../constants/productConstants";

export const productReducer = (state, action) => {
  switch (action.type) {
    case PRODUCT_LIST_REQUEST:
      return { loading: true, products: [] };
    case PRODUCT_LIST_SUCCESS:
      return { loading: false, products: action.payload };
    case PRODUCT_LIST_FAIL:
      return { loading: false, error: action.payload };
    case PRODUCT_DETAILS_REQUEST:
      return { loading: true, ...state };
    case PRODUCT_DETAILS_SUCCESS:
      return { loading: false, product: action.payload };
    case PRODUCT_DETAILS_FAIL:
      return { loading: false, error: action.payload };
    default:
      return state;
  }
};


ProductRoutes.js

const express = require("express");
const router = express.Router();
const Product = require("../models/ProductModel");

router.get("/", async (req, res) => {
  try {
    const products = await Product.find({});
    res.json(products);
  } catch (error) {
    console.log(error);
  }
});

router.get("/:id", async (req, res) => {
  try {
    const product = await Product.findById(req.params.id);
    if (product) {
      res.json(product);
    } else {
      res.status(404).json({ message: "Product not found" });
    }
  } catch (error) {
    console.log(error);
  }
});

module.exports = router;


经过长时间的努力我终于解决了,

我的 ProductReducer 设置错误,真实形式是

export const productReducer = (state, action) => {
  switch (action.type) {
    case PRODUCT_LIST_REQUEST:
      return { ...state, loading: true, products: [] };
    case PRODUCT_LIST_SUCCESS:
      return { ...state, loading: false, products: action.payload };
    case PRODUCT_LIST_FAIL:
      return { loading: false, error: action.payload };
    case PRODUCT_DETAILS_REQUEST:
      return { ...state, loading: true };
    case PRODUCT_DETAILS_SUCCESS:
      return { ...state, loading: false, product: action.payload };
    case PRODUCT_DETAILS_FAIL:
      return { loading: false, error: action.payload };
    default:
      return state;

为所有成功的情况添加“...state”,这解决了问题,但在 Redux(不是钩子)中,它在没有 ...state neeeded 的情况下工作,这令人困惑。