【问题标题】:Reactjs Routing and useParams HookReactjs 路由和 useParams Hook
【发布时间】:2021-06-26 02:02:30
【问题描述】:

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

UseFetch.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 />} />

【问题讨论】:

  • 似乎路由呈现了一个详细页面,而不是一个未找到的页面,但也许您脱节的 sn-ps 使您难以理解您的代码。你能分享Minimal, Complete, and Reproducible Code Example吗?您能否也澄清一下问题是什么,包括复制步骤?
  • @DrewReese 嘿,我附上了 codesanbox 链接,这里为什么产品变量返回为 null,当我在根页面 url 中输入像 /2 这样的可用 id 时。错误说“无法读取 null 的属性名称”link

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


【解决方案1】:

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

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>
);

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

另一个问题是当url 更新并再次运行效果时,您的useFetch 挂钩不会“重置”loading 状态。这里的修正是给一个初始为假的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 };
}

【讨论】:

  • const { data: { products }, // &lt;-- destructure products instead of rename data error, loading } = useFetch(products/${id}); 这里的获取 url 'localhost:3001/products/2' 应该返回一个像 { "id": 2, "category": "shoes", "image": "shoe2.jpg", "name": "Climber", "price": 78.99, "skus": [ { "sku": "28", "size": 8 }, { "sku": "29", "size": 9 } ], "description": "." } 这样的单个产品而不是一个产品数组,抱歉我找不到格式化代码块的方法
  • @NawazMohamed 没关系,cmets 并不是真正用于代码 sn-ps。如果fetch 确实只返回单个项目对象,那么我将其称为product,然后“找不到页面”检查为if (!product) return &lt;PageNotFound /&gt;,您可以删除过滤器,因为您已经拥有product要渲染的对象。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-13
  • 1970-01-01
  • 2020-04-13
  • 2021-11-14
  • 1970-01-01
  • 2023-02-06
  • 1970-01-01
相关资源
最近更新 更多