You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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:

  1. You're directly modifying state instead of using setState: React relies on setState() to track state changes and trigger re-renders. When you do this.state.cardWidth = width, React has no idea the state updated, so the other cards never get the new width value.
  2. Timing mismatch in rendering: On the first render, the first card's componentDidMount hasn't run yet—so this.state.cardWidth is still undefined when 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, calling setState tells 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 maxWidth prop once the width value exists, preventing undefined from breaking styles.
  • CSS Flexbox handles sizing natively: The flex: 1 1 15rem rule ensures each card grows/shrinks evenly to fill the row, keeping all cards the same width.

内容的提问来源于stack exchange,提问作者Steven

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 10:57:56