React实现结果卡片统一尺寸遇问题:回调传宽值为undefined求助
Hey there! Let's break down what's going on here and fix that card width issue for you.
First, Let's Diagnose the Problem
Your core issue comes from two key mistakes in how you're handling React state and rendering:
- You're directly modifying state instead of using
setState: React relies onsetState()to track state changes and trigger re-renders. When you dothis.state.cardWidth = width, React has no idea the state updated, so the other cards never get the new width value. - Timing mismatch in rendering: On the first render, the first card's
componentDidMounthasn't run yet—sothis.state.cardWidthis stillundefinedwhen the other cards are rendered.
Fix 1: Correct State Updates & Rendering Logic
Let's rewrite your parent component to use proper state management and handle the initial undefined state:
import React from 'react'; import Card from './Card.js'; class Body extends React.Component { constructor(props){ super(props); this.state = { cardWidth: undefined }; this.cardWidth = this.cardWidth.bind(this); }; cardWidth(width) { // Use setState to update state and trigger re-render this.setState({ cardWidth: width }); console.log('Card width captured:', width); }; render() { return ( <div className="body" style={body}> <Card cardWidth={this.cardWidth}/> {/* Conditionally render cards with correct width once state is set */} {this.state.cardWidth ? ( <> <Card style={{maxWidth: `${this.state.cardWidth}px`}}/> <Card style={{maxWidth: `${this.state.cardWidth}px`}}/> </> ) : ( {/* Fallback to default styles until width is captured */} <> <Card/> <Card/> </> )} </div> ); }; }; const body = { display: "flex", margin: "0% 10%", flexWrap: "wrap", gap: "1rem" // Add gap for better spacing }; export default Body;
And adjust the child component to ensure styles merge correctly and width is captured reliably:
import React from 'react'; class Card extends React.Component { constructor(props){ super(props); this.cardref = React.createRef(); }; componentDidMount() { if (this.props.cardWidth) { // Add a tiny delay to ensure DOM is fully rendered before measuring setTimeout(() => { this.props.cardWidth(this.cardref.current.offsetWidth); }, 0); }; }; render() { // Merge default card styles with props styles (props takes priority) const combinedStyle = { ...card, ...this.props.style }; return ( <div className="card" style={combinedStyle} ref={this.cardref}> <div className="card-body"> <h5 className="card-title" style={{fontSize: "100%"}}>Card title</h5> <p className="card-text" style={pText}>Some quick example text to build on the card title and make up the bulk of the card's content.</p> <a href="#" className="btn btn-primary" style={{fontSize: "80%"}}>Go somewhere</a> </div> </div> ); }; }; const card = { fontSize: "90%", flexBasis: "15rem", minWidth: "10rem", flexShrink: 1, flexGrow: 1, border: "1px solid #eee", borderRadius: "8px", padding: "1rem" }; const pText = { display: "block", textOverflow: "ellipsis", overflow: "hidden", height: "4rem", fontSize: "80%" }; export default Card;
Fix 2: Even Better—Use CSS Flexbox Directly
You don't need JavaScript to make all cards the same width! Flexbox can handle this natively. Ditch the state passing entirely and use this simpler CSS approach:
/* Replace inline styles with a CSS class for better maintainability */ .body { display: flex; margin: 0 10%; flex-wrap: wrap; gap: 1rem; } .card { font-size: 90%; flex: 1 1 15rem; /* Shorthand for flex-grow:1, flex-shrink:1, flex-basis:15rem */ min-width: 10rem; border: 1px solid #eee; border-radius: 8px; padding: 1rem; }
This will automatically make all cards in a row the same width, filling the available space, without any JS hacks. It's cleaner and more performant.
Why This Works
setState()triggers re-renders: When the first card's width is captured, callingsetStatetells React to re-render the parent component, passing the new width to the other cards.- Conditional rendering avoids undefined: We only render cards with the
maxWidthprop once the width value exists, preventingundefinedfrom breaking styles. - CSS Flexbox handles sizing natively: The
flex: 1 1 15remrule ensures each card grows/shrinks evenly to fill the row, keeping all cards the same width.
内容的提问来源于stack exchange,提问作者Steven

