Yoskutik
Yoskutik

Reputation: 2079

React.js: Using components of class in constructor

Here's the code:

export default class Collage extends React.Component {
    constructor() {
        super();
        this.state = {
            images: [
                <this.Image src="" />,
            ],
        };
    }

    Image = ({ src }) => (
        <img className="collage__img" alt="" src={src} onTransitionEnd={evt => evt.target.remove()} />
    );

    render() {
        return (
            <div className="collage">
                {this.state.images}
            </div>
        );
    }
}

All I want is to generate a list of images in .collage block before everything is rendered. As you see, I tried to create images in the constructor. But this way I get an error:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.

But if I declare Image in the constuctor (without using this) everything works fine. Such a strange behavior.

Could you tell, is there another way to generate data before render?

Upvotes: 0

Views: 891

Answers (4)

GBourke
GBourke

Reputation: 1953

Create your Image component class outside of College.js. Also remember to add in a "key" attribute on JSX which are stored in array to help react run faster.

Codesandbox

Image.js

import React from "react";
const Image = ({ src }) => (
  <img
    className="collage__img"
    alt=""
    src={src}
    onTransitionEnd={evt => evt.target.remove()}
  />
);

export default Image;

College.js

import React from "react";
import Image from "./Image";

export default class Collage extends React.Component {
  constructor() {
    super();
    this.state = {
      images: [
        <Image
          key="a356f8ff-0fc4-4c00-afb4-8ce60fcc210e"
          src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Test-Logo.svg/1200px-Test-Logo.svg.png"
        />
      ]
    };
  }

  render() {
    return <div className="collage">{this.state.images};</div>;
  }
}

App.js

import React from "react";
import "./styles.css";
import Collage from "./College";

export default function App() {
  return (
    <div className="App">
      <Collage />
    </div>
  );
}

Upvotes: 1

Omar Sy
Omar Sy

Reputation: 496

I don't know why you want to put your components in the state but this is not optimized. I think the best way to do that is somethink like this:

  export default class Collage extends React.Component {
        constructor() {
            super();
            this.state = {
                images: [
                    {src: ""}
                ],
            };
        }
    
        Image = ({ src }) => (
            <img className="collage__img" alt="" src={src} onTransitionEnd={evt => evt.target.remove()} />
        );
    
        render() {
            return (
                <div className="collage">
                    {this.state.images.map(this.Image)}
                </div>
            );
        }
    }

Upvotes: 1

tmhao2005
tmhao2005

Reputation: 17494

I don't know why you don't put <Image /> as separate component since it doesn't depend on anything in your class context as below:

const Image = ({ src }) => (
  <img className="collage__img" alt="" src={src} onTransitionEnd={evt => evt.target.remove()} />
);

class Collage extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
        images: [
          <Image src="" />
        ],
    };
  }

  render() {
    return (
        <div className="collage">
          {this.state.images}
        </div>
    );
  }
}

Upvotes: 1

Jayshree Wagh
Jayshree Wagh

Reputation: 80

You can use react lifecycle method componentWillMount() which will generate a list of images in .collage block before everything is rendered.

Upvotes: -1

Related Questions