Material-UI: either `image` or `src` property must be specified

老子叫甜甜 提交于 2021-02-18 22:54:52

问题


Its looks like CardMedia need an image while component is created. Since I am pulling the image data via componentDidMount (RestAPI) then the component is already mount.

componentDidMount() {
   // get all items via category ID and owner ID 
    const restApi = new API({ url: 'api' })
    restApi.createEntity({ name: 'items' })
    // api/items/<categoryId>/<ownerId>
    restApi.endpoints.items.getTwo({ id_a: this.props.categoryId, id_b: this.props.ownerId }).then(({ data }) => this.setState({ appData: data }))
}



render() {
    const { classes } = this.props;

    let classNameHolder = [classes.redAvatar, classes.greenAvatar, classes.blueAvatar, classes.purpleAvatar];
    this.state.appData.map(element => {
        this.state.images.push(element.imageUrl);
    });

    return (
        < Card >
            <CardHeader
                avatar={
                    <Avatar aria-label="Recipe"
                        className={classNameHolder[Math.floor(Math.random() * classNameHolder.length)]}>
                        {this.props.userName.charAt(0).toLocaleUpperCase()}
                    </Avatar>}
                title={this.props.userName} disableTypography={true} />
            <CardActionArea disabled={this.state.images.length === 1 ? true : false}>
                <CardMedia
                    id={this.props.ownerId}
                    className={classes.media}
                    image={this.state.images[this.state.imageIndex]}
                    onClick={this.handleOnClick} />
            </CardActionArea>
        </Card >
    );
  }
 }

I can move the all API one level up so I use the props in order to pass data image but I would like to know if you guys have any some elegant solution .

Thanks


回答1:


or you can do a simple check in the component itself so to avoid resetting the state, display a mui spinner for instance while content loads this will fix the warning and display nice feedback to the user

<>
  {imgUrl ? (
    <CardMedia
      component="img"
      alt="Contemplative Reptile"
      height="140"
      src={imgUrl}
      title="Contemplative Reptile"
    />
  ) : (
    <Spinner />
  )}
</>



回答2:


Not sure what appData contains, but I made some changes to your code, hopefully is gonna give you a better understanding.

render() {
    const { classes } = this.props;

    let classNameHolder = [classes.redAvatar, classes.greenAvatar, classes.blueAvatar, classes.purpleAvatar];
/*
    // you don't save the return of the next array anywhere, so this map is doing nothing.
    this.state.appData.map(element => {
        // you cannot redefine state like this, to redefine state you have to use setState
        this.state.images.push(element.imageUrl);
    });
*/

    const imagesUrl = this.state.appData.map(el => el.imageUrl);
    return (
        < Card >
            <CardHeader
                avatar={
                    <Avatar aria-label="Recipe"
                        className={classNameHolder[Math.floor(Math.random() * classNameHolder.length)]}>
                        {this.props.userName.charAt(0).toLocaleUpperCase()}
                    </Avatar>}
                title={this.props.userName} disableTypography={true} />
{/* This part I don't undestand much what you try to do, I can propose use map of imagesUrl. But to help you more I need to know more info about what you try to do here */}
            {/* Let's not render this part of the component instead of disabled it */}
            {imagesUrl.length > 0 &&
              <CardActionArea disabled={this.state.images.length === 1 ? true : false}>
                <CardMedia
                    id={this.props.ownerId}
                    className={classes.media}
                    image={this.state.images[this.state.imageIndex]}
                    onClick={this.handleOnClick} />
              </CardActionArea>
            }
        </Card >
    );
  }
 }

Suggestion reviewing your array appData, maybe is good idea print the content after you retrieve it, let's see an example.

  const { classes } = this.props;

  const classNameHolder = [classes.redAvatar, classes.greenAvatar, classes.blueAvatar, classes.purpleAvatar];
  const AvatarComponent = (
    <Avatar aria-label="Recipe"
      className={classNameHolder[Math.floor(Math.random() * 
  classNameHolder.length)]}>
      {this.props.userName.charAt(0).toLocaleUpperCase()}
    </Avatar>
  );

  return (<div className="Cards">
    {this.state.appData.map(data => (
      <Card>
        <CardHeader avatar={AvatarComponent} title={this.props.userName} disableTypography={true} />

        <CardActionArea disabled={data.imageUrl !== ''}>
          <CardMedia
            id={this.props.ownerId}
            className={classes.media}
            image={this.state.images[this.state.imageIndex]}
            onClick={this.handleOnClick} />
        </CardActionArea>
      </Card>
    ))}
  </div>);

Like this, we wait to get the async data before rendering the component, previously I changed, instead of disabling the component just prevent render it if you still don't have the images.




回答3:


I faced with the same problem and solved it by initializing that state in the constructor. The "CardMedia" has 'image' attribute which needs to receive a String, but when you want to send a state there, something like image={this.state.images[this.state.imageIndex]}, it returns null in the first rending time. You can solve the problem by adding the below code in your constructor.

this.state = {
      images: 'some default address/link/values'
    };



回答4:


const photos = [
  "http://i.photo.cc/300?img=1",
  "http://i.photo.cc/300?img=2",
  "http://i.photo.cc/300?img=3",
  "http://i.photo.cc/300?img=4"
];

{photos.map(photo => (
   <CardMedia image={photo} />
))}

Providing image prop with the direct URL also works!



来源:https://stackoverflow.com/questions/55290345/material-ui-either-image-or-src-property-must-be-specified

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!