React Hook useParams调用报错求助:非React函数组件或自定义Hook函数中调用useParams
Let's break down what's going wrong here and how to fix it step by step:
Why the Error Happens
React's Hook rules are strict: Hooks like useParams can only be called at the top level of a React function component or inside a custom Hook (functions starting with use). Your fetchMentors function is a regular helper function, not a component or custom Hook, so calling useParams inside it violates this rule.
Step-by-Step Fixes
1. Move useParams to the Component Top Level
First, extract categoryId at the very top of your MentorList component, so it lives in the component's top scope where Hooks are allowed:
function MentorList() { // Pull categoryId at the component's top level const { categoryId } = useParams(); // ... rest of your state hooks
2. Update fetchMentors to Use the Top-Level categoryId
Modify your fetchMentors function to reference the already-extracted categoryId instead of trying to call useParams inside it:
const fetchMentors = () => { // Now we use the categoryId from the component's top scope if (categoryId) { console.dir('test'); Axios.get(`https://localhost:4000/view-by-category/${categoryId}`) .then((response) => { if (response.data.status === "ok") { setMentorsResult(Object.keys(response.data.result)); setLoading(false); } else { setError(response.data.error); setLoading(false); } }) .catch(err => { console.dir(err); setError("There was an error with the request. Please try again later."); setLoading(false); }); } };
3. Add categoryId to useEffect Dependencies
Since fetchMentors relies on categoryId, we need to include it in the useEffect dependency array. This ensures that when the user navigates to a different category (and categoryId changes), fetchMentors runs again to load the correct set of mentors:
useEffect(() => { // ... your fetchCategories and fetchMentors functions fetchCategories(); fetchMentors(); }, [categoryId]); // Add categoryId here to trigger re-runs on change
4. Fix the categories Rendering Issue
You're using forEach to generate category links, but forEach doesn't return anything—so your categories variable will be undefined. Replace forEach with map to return a valid array of JSX elements, and add a required key prop for React list rendering:
const categories = categoriesResult.map(item => { return <div className="row" key={item.id}> <NavLink to={`/mentors/${item.id}`} exact>{item.name}</NavLink> </div>; });
Full Corrected Code
import React, { useState, useEffect } from 'react'; import Axios from "axios"; import {NavLink, useParams} from "react-router-dom"; import Spinner from '../../Spinner'; function MentorList() { // Extract categoryId at component top level const { categoryId } = useParams(); const [categoriesResult, setCategoriesResult] = useState([]); const [mentorsResult, setMentorsResult] = useState([]); const [categoriesToId, setCategoriesToId] = useState({}); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); useEffect(() => { const fetchCategories = () => { Axios.get("https://localhost:4000/categories") .then((response) => { if (response.data.status === "ok") { setCategoriesResult(Object.keys(response.data.result)); setCategoriesToId(response.data.result); setLoading(false); } else { setError(response.data.error); setLoading(false); } }) .catch(err => { console.dir(err); setError("There was an error signing you in. Please try again later."); setLoading(false); }); }; const fetchMentors = () => { if (categoryId) { console.dir('test'); Axios.get(`https://localhost:4000/view-by-category/${categoryId}`) .then((response) => { if (response.data.status === "ok") { setMentorsResult(Object.keys(response.data.result)); setLoading(false); } else { setError(response.data.error); setLoading(false); } }) .catch(err => { console.dir(err); setError("There was an error with the request. Please try again later."); setLoading(false); }); } }; fetchCategories(); fetchMentors(); }, [categoryId]); // Add categoryId to dependencies if (loading) { return <Spinner />; } if (error) { return <div className="error-message">{error}</div>; } const mentors = mentorsResult.map(mentor => { return <div className="row" key={mentor.id}> <p className="mentor-profile-name">{mentor.name}</p> <p className="mentor-profile-bio">{mentor.shortBio}</p> <NavLink to={`/mentor/${mentor.id}`} exact>View Profile</NavLink> </div> }) const categories = categoriesResult.map(item => { return <div className="row" key={item.id}> <NavLink to={`/mentors/${item.id}`} exact>{item.name}</NavLink> </div>; }); return <div className="container"> <div className="col-3"> {categories} </div> <div className="col-9"> {mentors} </div> </div> }; export default MentorList;
Bonus: Improved Error Handling
I added a dedicated render for error messages so users can see when something goes wrong, instead of the app silently failing.
内容的提问来源于stack exchange,提问作者Chris Hansen

