Реагирующие крючки не работают между маршрутами, но работают при обновлении

#reactjs #react-hooks #e-commerce #context-api

#reactjs #реагирующие крючки #электронная коммерция #реагировать-контекст

Вопрос:

Я создаю типичную электронную коммерцию, по которой вы нажимаете на продукт, а затем получаете конкретную подробную страницу об этом. я хочу увидеть подробную страницу, поэтому я нажимаю на product, но это дает мне неопределенную ошибку, однако, когда я обновляю страницу, она возвращает то, что я хочу, просто проблема, когда я меняю ее с помощью route.

Не удается прочитать свойство ‘image’ undefined , это означает, что product не определен, 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 amp;amp; 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 amp;amp; (
                  <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;


  

Комментарии:

1. 1. Для вашего вызова useEffect массив зависимостей должен содержать match listProductDetails параметры и (в противном случае ваш useEffect не будет отозван при match.params обновлении). 2. На вашем месте, если вы не хотите снова изобретать колесо или учиться чему-то самостоятельно, используйте что-то вроде react-query , вы удалите примерно половину написанного вами кода, и ваш код будет менее подвержен ошибкам.

2. Я их уже делал, но ничего не менял, в любом случае, я решил это окончательно, вы можете увидеть, как я это сделал ниже, и я попробую react-query , это звучит полезно, большое спасибо!

Ответ №1:

После долгих усилий мне удалось это решить,

Мой 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;
  

Добавлено «… состояние» для всех успешных случаев, это решаемая проблема, но в Redux (не в hooks) оно работает без необходимости … состояния, что сбивает с толку.