Reactjs 路由和 useParams Hook

时间:2021-03-30 03:31:20

标签: javascript reactjs react-hooks react-router-dom fetch-api

当我尝试使用路由导航到我的页面时,我正在从 API 获取数据并将数组存储在“products”变量中并引用请求 URL 中的占位符参数,但是当我尝试导航到像“http:/ /localhost:3000/shoes/4' 应该返回带有未找到页面文本的页面,而不是详细信息页面。请说明为什么没有发生这种情况。
Json 服务器在 3001 端口

使用Fetch.js

import { useState, useEffect } from "react";

const baseUrl = "http://localhost:3001/";

export default function useFetch(url) {
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(`${baseUrl}${url}`)
      .then((response) => {
        return response.json();
      })
      .then((data) => {
        setData(data);
      })
      .catch((error) => {
        setError(error);
      })
      .finally(() => {
        setLoading(false);
      });
  }, [url]);

  return { data, error, loading };
}

产品数组

{
  "id": 1,
  "category": "shoes",
  "image": "shoe1.jpg",
  "name": "Hiker",
  "price": 94.95,
  "skus": [
    { "sku": "17", "size": 7 },
    { "sku": "18", "size": 8 }
  ],
  "description": "This rugged boot will get you up the mountain safely."
},
{
  "id": 2,
  "category": "shoes",
  "image": "shoe2.jpg",
  "name": "Climber",
  "price": 78.99,
  "skus": [
    { "sku": "28", "size": 8 },
    { "sku": "29", "size": 9 }
  ],
  "description": "Sure-footed traction in slippery conditions."
},
{
  "id": 3,
  "category": "shoes",
  "image": "shoe3.jpg",
  "name": "Explorer",
  "price": 145.95,
  "skus": [
    { "sku": "37", "size": 7 },
    { "sku": "38", "size": 8 },
    { "sku": "39", "size": 9 }
  ],
  "description": "Look stylish while stomping in the mud."
},
{
  "id": 4,
  "category": "headphone",
  "image": "headphone3.jpg",
  "name": "headphone red",
  "price": 100,
  "description": "Red colored headphone"
},
{
  "id": 5,
  "category": "headphone",
  "image": "headphone2.jpg",
  "name": "headphone blue",
  "price": 90,
  "description": "Blue colored headphone"
},
{
  "id": 6,
  "category": "headphone",
  "image": "headphone1.jpg",
  "name": "headphone black",
  "price": 80,
  "description": "Black colored headphone"
}

获取数据到产品数组

const { category, id } = useParams();
const { data: products, error, loading } = useFetch(
    `products?category=${category}&id=${id}`
);

如果产品数组为空,则定向到找不到页面

if (products.length === 0) {
    return <PageNotFound />;
}

使用路由定向到详细信息页面

<Route path="/:category/:id" element={<Detail />} />

1 个答案:

答案 0 :(得分:0)

您将返回的 data 对象重命名为 products,我确定您打算从 products 中解构嵌套的 data 数组。

const {
  data: { products }, // <-- destructure products instead of rename data
  error,
  loading
} = useFetch(`products/${id}`);

现在 products.length 检查实际上将测试一个数组对象。

if (!products.length) {
  return <PageNotFound />;
}

剩下的问题是呈现特定的“产品”或匹配的“产品”数组。这是您的代码有点奇怪的地方,因为您将类别和 id 都传递给 API 端点,而 fetch 不执行任何响应处理/过滤,它返回整个 JSON 响应。我在这里假设 UI 将处理 products 数组以匹配 categoryid 的产品。

if (!products.length) return <PageNotFound />;

const product = products.find(
  (product) => product.category === category && `${product.id}` === id
);

return (
  <div id="detail">
    <h1>{product.name}</h1>
    <p>{product.description}</p>
    <p id="price">${product.price}</p>
    <img src={`/images/${product.image}`} alt={product.category} />
  </div>
);

Edit reactjs-routing-and-useparams-hook

注意:如果您想在 useFetch 钩子中执行此操作并仍然返回一个数组,请使用 Array.prototype.filter 使用相同的条件并记住任何可呈现的内容仍将在一个数组中,因此您将需要映射结果或从 products 数组的前/后移动/弹出结果。

另一个问题是当 useFetch 更新并且效果再次运行时,您的 loading 钩子不会“重置”url 状态。此处的解决方法是给出初始错误的 loading 状态,并在获取开始时将其设置为真。

export default function useFetch(url) {
  const [data, setData] = useState(products);
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(false); // <-- default false

  useEffect(() => {
    setLoading(true); // <-- toggle true when starting fetch
    fetch(`${baseUrl}${url}`)
      .then((response) => {
        return response.json();
      })
      .then((data) => {
        setData(data);
      })
      .catch((error) => {
        setError(error);
      })
      .finally(() => {
        setLoading(false);
      });
  }, [url]);

  return { data, error, loading };
}