在React中使用React-Router-DOM的useLocation接收传递的数据会导致手动输入的链接不起作用

问题描述 投票:0回答:2

最初,如果手动输入的链接错误(例如:

"/characters/test"
),它可以工作,但如果正确,它仍然会重定向到错误 404。如果从
Character
组件单击链接,则它可以正常工作。这意味着无论我在浏览器上手动放置什么链接,都会导致错误 404。

我一直在尝试在

CharacterCard
组件上实现错误 404 重定向,方法是比较传递的 id - 'name'(如
useParams
)并将其与传递的另一个组件中的 characterId 列表进行比较(如果匹配) ,那么就可以正常渲染了,如果没有的话,就会重定向到错误404。

由于我使用的是 React-Router v6,据我所知,我无法正常传递 props。经过一番研究,我发现我可以使用

Link
状态将数据传递到它链接到的另一个组件,并使用
useLocation
获取传递的数据。我成功地将数据传递给组件,但随后导致了我上面提到的问题。

人物

import { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { PuffLoader } from "react-spinners";

const API_URL = 'something.somethin.com';
const CDN_URL = 'something2.somethin2.com';

export const Characters = () => {
  const [characters, setCharacters] = useState([]);
  const [isLoaded, setIsLoaded] = useState(false);

  const validCharacterIds = characters.map((character) => character.id);

  const getCharacters = async () => {
    try {
      const response = await fetch({API_URL});
      const charactersData = await response.json();

      const characterDetailsData = await Promise.all(
        charactersData.map(async (character) => {
          const characterDetails = await getCharacterDetails(character);
          return characterDetails;
        })
      );

      setCharacters(characterDetailsData);
      console.log('characterDetaildata: ', characterDetailsData);
    } catch (error) {
      console.log("Error fetching Characters:", error);
    }
  };

  const getCharacterDetails = async (character) => {
    //code for getCharacterDetails

    setIsLoaded(true);
    return { ...characterDetailsData, id:character, icon: imageData };
  };

  useEffect(() => {
    getCharacters();
    document.title = "Characters | Website Title"
  }, []);

  return (
    <div className='w-full py-[3rem] px-4 bg-white'>
      //Character Card that when clicked opens CharacterCard Component
      <div className='w-[500px] md:w-[1200px] mx-auto grid grid-cols-1 md:grid-cols-5 gap-5'>
        {isLoaded ? (
          (characters.length > 0) ? (
            characters.map((character) => (
              <Link key={character.name} to={`/characters/${character.id}`} state={{charIds: validCharacterIds}} className='w-full shadow-xl flex flex-col my-4 rounded-lg hover:scale-105 duration-300'>
                <div className={
                  character.rarity === 4
                    ? "bg-gradient-to-b from-[#5e5789] to-[#9c75b7]"
                    : character.rarity === 5
                      ? "bg-gradient-to-b from-[#945c2c] to-[#b27330]"
                      : ''
                }>
                  <img className='w-[300px] h-[250px] object-cover' src={character.icon} alt={character.name + ' Icon'} />
                </div>
                <div>
                  <h4 className='text-2xl font-bold text-center py-8'>{character.name}</h4>
                </div>
              </Link>
            ))
          ) : (
            <div className='empty'>
              <h2>No Characters Found!</h2>
            </div>
          )
        ) : (
          <div className="flex justify-center items-center">
            <PuffLoader color="#36d7b7" />
          </div>
        )}
      </div>
    </div>
  );
};

人物卡

import React, { useEffect, useState } from 'react';
import { useNavigate, Navigate, useParams, useLocation } from 'react-router-dom';
import { PuffLoader } from 'react-spinners';
import Carousel from './Carousel';

const CDN_URL = 'somethin.somethin.com';
const API_URL = 'somethin2.somethin2.com';

export const CharacterCard = () => {
  const [characterDetails, setCharacterDetails] = useState(null);

  const navigate = useNavigate();
  const { name } = useParams();
  const location = useLocation();
  let validCharIds = location.state;
  console.log(location);

  const getCharacterDetails = async (characterName) => {
    //rest of the api fetch goes here
  };


  useEffect(() => {
    const checkValidCharId = async () => {
      if (!validCharIds || !validCharIds.charIds || !validCharIds.charIds.includes(name)) {
        // Redirect to error 404 if the name is not in the validCharIds array or empty
        navigate('*');
        console.log('error 404');
      } else {
        // Fetch character details only if the name is valid
        await getCharacterDetails(name);
      }
    };

    checkValidCharId();
  }, [name, validCharIds, navigate]);



  if(characterDetails) {
      return (
      //Render component normally

    );
  }

  else {
    return (
      <div className='flex justify-center items-center'>
         <Navigate to='*'/>
      </div>
    )
  }

};
javascript reactjs react-hooks react-router react-router-dom
2个回答
0
投票

您面临的问题可能与您在 Link 组件中使用 state 属性的方式有关。您可以使用不同的方法将数据传递到CharacterCard 组件,而不是依赖Link 组件的state 属性。您可以更新 Link 组件的 to 属性,将角色 id 作为参数包含在路由中:

<Link key={character.name} to={`/characters/${character.id}?charIds=${validCharacterIds.join(',')}`} className='w-full shadow-xl flex flex-col my-4 rounded-lg hover:scale-105 duration-300'>

这样,您就可以将 charId 作为查询参数传递到 URL 中。然后,在CharacterCard组件中,您可以使用useParams检索此查询参数:

const { name } = useParams();
const charIds = new 
URLSearchParams(useLocation().search).get('charIds')?.split(',');

useEffect(() => {
  const checkValidCharId = async () => {
    if (!charIds || !charIds.includes(name)) {
      navigate('*');
  console.log('error 404');
} else {
  
  await getCharacterDetails(name);
 }
};

checkValidCharId();
}, [charIds, name, navigate]);

这样,无论是点击链接还是手动输入,CharacterCard 组件中都应该有必要的数据,并且 404 重定向逻辑应该按预期工作。


0
投票

路由路径参数始终是字符串类型。我怀疑

character.id
是一个非字符串值,并且在作为
name
路由路径参数传递时会被字符串化。

建议进行类型不敏感的比较。字符串到字符串很常见,因为它涵盖非数字字符串,例如

"0123456789abcdef"
,以及类似数字的字符串,例如
5
vs
"5"

// Create array of id strings
const validCharacterIds = characters.map(({ id }) => id).map(String);
// or const validCharacterIds = characters.map(({ id }) => String(id));

...

{isLoaded ? (
  characters.length ? (
    characters.map((character) => (
      <Link
        key={character.name}
        to={`/characters/${character.id}`}
        state={{ charIds: validCharacterIds }} // <-- pass array of id strings
        className='....'
      >
        ...
      </Link>
    ))
  ) : (
    <div className='empty'>
      <h2>No Characters Found!</h2>
    </div>
  )
) : (
  <div className="flex justify-center items-center">
    <PuffLoader color="#36d7b7" />
  </div>
)}
export const CharacterCard = () => {
  const [characterDetails, setCharacterDetails] = useState(null);

  const navigate = useNavigate();
  const { name } = useParams(); // <-- name is string type
  const location = useLocation();

  const { charIds } = location.state || {};

  ...

  useEffect(() => {
    const checkValidCharId = async () => {
      if (!charIds.includes(name)) {
        // Redirect to error 404 if the name is not in the validCharIds array or empty
        navigate('*');
      } else {
        try {
          // Fetch character details only if the name is valid
          setCharacterDetails(await getCharacterDetails(name))
        } catch(error) {
          // handle any errors
        };
      }
    };

    checkValidCharId();
  }, [name, charIds, navigate]);

  ...
};
© www.soinside.com 2019 - 2024. All rights reserved.