React.js嵌套API数据获取问题:无法渲染userApplications数组内的字段
userApplications Array in React Let's break down why your Application ID and Name fields aren't rendering, and fix it step by step:
Core Issue: userApplications is an Array, Not a Single Object
Looking at your sample API response, userApplications is wrapped in square brackets []—that means it's an array of application objects, not a single object. When you try to access item.userApplications.applicationId, you're trying to get a property directly from an array, which doesn't exist (arrays use indexes to access elements, not dot notation for nested objects).
Fix 1: Access Array Elements Correctly
If each user has only one application:
Use array index notation with optional chaining (?.) to safely access the first element in the array (this prevents errors if the array is empty or undefined):
<li> Application ID: <span>{item.userApplications?.[0]?.applicationId}</span> </li> <li> Application Name: <span>{item.userApplications?.[0]?.application}</span> </li> <li> Role: <span>{item.userApplications?.[0]?.role}</span> </li>
If users can have multiple applications:
Loop through the userApplications array to render each application's details:
<div className="user-applications"> <h6>Applications:</h6> {item.userApplications.map(app => ( <div key={app.applicationId} className="application-item"> <li> Application ID: <span>{app.applicationId}</span> </li> <li> Application Name: <span>{app.application}</span> </li> <li> Role: <span>{app.role}</span> </li> </div> ))} </div>
Fix 2: Fix Other Minor Code Issues
Your code has a few small bugs that might cause unexpected behavior:
- JSX uses
className, notclass: Replace all instances ofclass="..."withclassName="..."(React doesn't recognize the HTMLclassattribute). - Duplicate
classNameon card-grid: Merge the two class names into one:<div className="card-grid font-poppins"> - Ensure
itemsis always an array: If your/api/usersendpoint returns a single user object (like your sample data), you need to wrap it in an array when setting state:
If the endpoint returns an array of users, leavesetItems(Array.isArray(result) ? result : [result]); // Wrap single user in arraysetItems(result)as-is.
Full Modified Code Snippet
Here's your code with all fixes applied:
import { useState, useEffect } from 'react'; function UserList() { const [items, setItems] = useState([]); const [applicationId, setapplicationId] = useState([]); const [isLoaded, setIsLoaded] = useState(false); const [error, setError] = useState(null); const [q, setQ] = useState(""); // Define search parameters (adjust based on your needs) const searchParam = ["firstName", "username", "lastName"]; useEffect(() => { fetch('/api/users') .then(res => res.json()) .then(result => { setIsLoaded(true); // Ensure items is always an array setItems(Array.isArray(result) ? result : [result]); setapplicationId(result.userApplications || []); }, error => { setIsLoaded(true); setError(error); }); }, []); function search(items) { return items.filter(item => { return searchParam.some(newItem => { // Add optional chaining to avoid errors if property doesn't exist return (item[newItem]?.toString().toLowerCase().indexOf(q.toLowerCase()) > -1); }); }); } if (error) { return <>{error.message}</>; } else if (!isLoaded) { return <>loading...</>; } else { return ( <div className="wrapper"> <div className="search-wrapper"> <label htmlFor="search-form"> <input type="search" name="search-form" id="search-form" className="form-control" placeholder="Search for..." value={q} onChange={e => setQ(e.target.value)} /> <span className="sr-only">Search Users here</span> </label> </div> <div className="card-grid font-poppins"> {search(items).map(item => ( <div key={item.userId} className="card"> <div className="card-content"> <div className="card-list card-body"> <h5 className="card-title">{item.firstName}</h5> <p className="card-text">{item.username}</p> <li> First Name: <span>{item.firstName}</span> </li> <li> Last Name: <span>{item.lastName}</span> </li> <li> User ID: <span>{item.userId}</span> </li> {/* Render single application details */} <li> Application ID: <span>{item.userApplications?.[0]?.applicationId}</span> </li> <li> Application Name: <span>{item.userApplications?.[0]?.application}</span> </li> <li> Role: <span>{item.userApplications?.[0]?.role}</span> </li> {/* Uncomment below to render multiple applications */} {/* <div className="user-applications"> <h6>Assigned Applications:</h6> {item.userApplications.map(app => ( <div key={app.applicationId} className="application-item"> <li> Application ID: <span>{app.applicationId}</span> </li> <li> Application Name: <span>{app.application}</span> </li> <li> Role: <span>{app.role}</span> </li> </div> ))} </div> */} </div> </div> </div> ))} </div> </div> ); } } export default UserList;
Key Notes
- The optional chaining operator (
?.) prevents runtime errors ifuserApplicationsis empty, undefined, or if an application object doesn't have the expected property. - Always add a unique
keyprop when mapping arrays in React (I useditem.userIdandapp.applicationIdas keys here to ensure React can efficiently update the DOM).
内容的提问来源于stack exchange,提问作者Peter

