React Hooks与Fetch逻辑优化及Material-UI Card集成问题咨询
Hey there! Let's break down your code step by step—first looking at the implementation logic, then fixing that nested property rendering issue when switching to Material-UI Cards.
1. Feedback on Your Current Implementation Logic
- Redundant Fetch Logic: You've got identical
fetchcalls in bothuseEffectandnextPerson. Extracting this into a reusable function will keep your code DRY (Don't Repeat Yourself), making maintenance easier if you ever need to adjust the API endpoint or add error handling. - Missing Loading State for Button Clicks: Right now, clicking the "click me" button doesn't trigger a loading state. Users might spam the button before new data loads, leading to unnecessary API calls. You should toggle the
loadingstate here too. - Incorrect Key Placement: You're setting the
keyprop on the<img>element, but React expects the key to be on the root element returned by yourmapcallback. This helps React efficiently update and re-render list items. - State Update Behavior: Your current
setItems(response.results)replaces the entireitemsarray with the new user. If your goal is to add users to the list instead of replacing them, usesetItems(prev => [...prev, ...response.results]). If replacing is intentional, that's totally fine—just wanted to flag this as a configurable behavior! - No Error Handling: There's no fallback for failed
fetchcalls. Adding a.catch()block will prevent silent failures and let you notify users if something goes wrong.
2. Fixing the Nested Property Rendering Issue with Material-UI Cards
The problem where {item.something.something} won't render is likely due to either incorrect key placement or missing loading state handling during button clicks. Let's rewrite your component with Material-UI's Card components to fix this, while addressing the feedback above.
Updated Code with Material-UI Cards
import React, { useState, useEffect } from 'react'; import { makeStyles } from '@material-ui/core/styles'; import Card from '@material-ui/core/Card'; import CardMedia from '@material-ui/core/CardMedia'; import CardContent from '@material-ui/core/CardContent'; import Typography from '@material-ui/core/Typography'; import Button from '@material-ui/core/Button'; const useStyles = makeStyles({ root: { minWidth: 275, margin: '1rem auto', maxWidth: 300, boxShadow: '0 2px 4px rgba(0,0,0,0.1)', }, media: { height: 200, backgroundSize: 'contain', backgroundColor: '#f5f5f5', }, content: { textAlign: 'center', }, button: { marginTop: '1rem', width: '100%', }, }); export default function SocialCards() { const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const classes = useStyles(); // Reusable function for fetching random users const fetchRandomUser = () => { setLoading(true); // Trigger loading state immediately fetch("https://randomuser.me/api") .then((response) => { if (!response.ok) throw new Error('Failed to load user data'); return response.json(); }) .then((response) => { // Replace existing user: setItems(response.results) // Uncomment below to add users to the list instead of replacing // setItems(prev => [...prev, ...response.results]); setItems(response.results); setTimeout(() => setLoading(false), 1000); // Keep loading animation }) .catch(err => { console.error(err); setLoading(false); // Optional: Show an error message to the user here }); }; useEffect(() => { fetchRandomUser(); }, []); if (loading) { return <div style={{ textAlign: 'center', marginTop: '2rem', fontSize: '1.2rem' }}>Loading ...</div>; } return ( <div style={{ padding: '1rem', maxWidth: '1200px', margin: '0 auto' }}> {items.map((item, i) => ( // Key is placed on the root Card element (correct React practice) <Card className={classes.root} key={i}> <CardMedia className={classes.media} image={item.picture.large} alt={`${item.name.first} ${item.name.last}`} /> <CardContent className={classes.content}> <Typography variant="h5" component="h2" gutterBottom> {item.name.first} {item.name.last} </Typography> <Typography color="textSecondary" variant="body1" gutterBottom> {item.email} </Typography> <Typography variant="body2" component="p"> {item.location.city}, {item.location.country} </Typography> <Button variant="contained" color="primary" className={classes.button} onClick={fetchRandomUser} > Get New User </Button> </CardContent> </Card> ))} </div> ); }
Key Fixes & Improvements:
- Reusable Fetch Logic:
fetchRandomUserhandles all API calls, including loading state management and error handling. - Proper Key Placement: The
keyis now attached to the<Card>element (the root of each mapped item), following React's list best practices. - Material-UI Card Integration: Uses
Card,CardMedia, andCardContentto build a polished social card layout, with seamless access to nested properties likeitem.name.firstanditem.picture.large. - Loading State for Button Clicks: Every button click triggers the loading state, preventing duplicate API requests.
- Error Handling: Added a
.catch()block to handle failed requests gracefully.
Now when you map over items, all nested properties should render correctly inside the Card components. If you want to accumulate users instead of replacing them, just swap the state update line in fetchRandomUser to use the spread operator with previous state.
内容的提问来源于stack exchange,提问作者GoOsT

