React.js: Using components of class in constructor - javascript

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?

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>
);
}

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>
);
}
}

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>
);
}
}

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

Related

Problem with loading local images added in object array in react

I made a #JS file called SliderImgs and put an array of objects in that which includes the properties of images of a slider:
const Images=[
{
src:'./files/new-banner-high.jpg',
alt:'banner',
order:'0'
}
]
export default Images
Then I imported this array to a component and then passed that (the array) from props to another component called Slider
import React, { Component } from 'react'
import Images from './SliderImgs'
import Slider from './Slider'
class Body extends Component {
render() {
return (
<div>
<div className="slider">
<Slider pictures={Images}/>
</div>
</div>
)
}
}
export default Body
and finally tried to Print the Image on the screen using the map() method, but I got this error:
Uncaught Error: Cannot find module './files/new-banner-high.jpg'
import React, { Component } from 'react'
class Slider extends Component {
constructor(props){
super(props);
this.state ={
pictures:[]
}
}
static getDerivedStateFromProps(props, state){
return {pictures : props.pictures }
;
}
render() {
return (
<div className="slidercontainer">
{this.state.pictures.map((picture, index)=> {
return(
<img src={require(picture.src)} alt={picture.alt} key={index} />
)
})}
</div>
)
}
}
export default Slider
Normally images should be kept in the public folder.
Move the files directory to the public folder.
Change the array as below.
const Images=[
{
src:'/files/new-banner-high.jpg',
alt:'banner',
order:'0'
}
]
Change the img element like below.
<img src={picture.src} alt={picture.alt} key={index} />

Add a child component with N number .of times inside parent Component (based on a state value)

I want to add a child-component ColorBox in return of parent component ColorBoxContainer based on No of times there is a value in state noOfBoxes: 16. I am trying doing using for-loop but I guess the code is wrong. Can someone help me out, how to achieve this?
import React, { Component } from 'react';
import ColorBox from './ColorBox';
class ColorBoxContainer extends Component {
constructor(props) {
super(props);
this.state = {
noOfBoxes: 16
}
}
render(){
return (
<div>
{for(i=0;i<this.state.noOfBoxes;i++){
<ColorBox />
}}
</div>
)
}
}
export default ColorBoxContainer;
import React, { Component } from 'react';
import ColorBox from './ColorBox';
class ColorBoxContainer extends Component {
constructor(props) {
super(props);
this.state = {
noOfBoxes: 16
}
}
render(){
return (
<div>
{
Array(this.state.noOfBoxes).fill().map((_,i) => <ColorBox key={i}/>)
}
</div>
)
}
}
export default ColorBoxContainer;
Create an array with the given element length and map it to your element:
<div>
{Array(this.state.noOfBoxes).fill().map((_, index) => (
<ColorBox key={index} />
))}
</div>
return Array.from({length: this.state.noOfBoxes}, (item, index) =>
<ColorBox />
)

How to pass component to onClick in react

import React from 'react'
export default () => {
function clickHandler() {
console.log('Button clicked')
}
return (
<div>
<button onClick={clickHandler}>Click</button>
</div>
)
}
In the above code we see that a function has been passed to the onClick.In the same way to the onClick I need to pass a diffrent component which is present in the same src. This component consists of a .js and a .css file.Could you please help me out with it. Thanks in advance
If you don't mind using classes instead of functions, your other component should look like this:
import React from 'react'
class ShowThisAfterClick extends React.Component {
return (
<div>
<p>This is what you want to show</p>
</div>
)
}
export default ShowThisAfterClick
And now you should update the component you've shown:
import React from 'react'
import ShowThisAfterClick from './where/you/put/the/ShowThisAfterClick.js'
class Main extends React.Component {
constructor(props){
super(props)
this.state = { isButtonClicked: false }
this.clickHandler = this.clickhandler.bind(this)
}
clickHandler() {
this.setState({ isButtonClicked: true })
}
render() {
const { isButtonClicked } = this.state
return (
<div>
<button onClick={ this.clickHandler }>Click</button>
{ isButtonClicked ? <ShowThisAfterClick /> : ''}
</div>
)
}
}
export default Main
If you want to keep using functions, then I would kindly suggest to read the manual, it is more than well written.

ReactJS - Show a preloader before the image is loaded

I have this component in which it is rendered data coming from a json. Everything is working right. But I would like to insert a loader: <i className="fa fa-spinner"></i> before the image is loaded. The loader should disappear afterwards as well. How I do?
import React, { Component } from 'react'
var data = require('./db.json');
class Biografy extends Component {
constructor(props) {
super(props)
this.state = {
photo: ""
}
}
componentDidMount() {
this.setState({
photo: data.biography[0].photo
})
}
render() {
return (
<div>
<i className="fa fa-spinner"></i>
<img src={this.state.photo && `/images/${this.state.photo}`} alt="" />
</div>
)
}
}
export default Biografy
Since it looks like you're using the state of this.state.photo to determine whether it's loading or not, you could simply add that conditional before rendering the i tag:
{!this.state.photo && <i className="fa fa-spinner"></i>}
img src has onLoad event that you need in this case.
Try this:
import React, { Component } from 'react'
var data = require('./db.json');
class Biografy extends Component {
state = {
photo: "",
loading: false
}
componentDidMount() {
this.setState({
photo: data.biography[0].photo
})
}
render() {
const style = this.state.loading ? {} : {visibility: 'hidden'}
const {loading} = this.state;
return (
<div>
{!loading && <i className="fa fa-spinner"></i>}
<img src={this.state.photo && `/images/${this.state.photo}`} alt=""
onLoad={() => this.setState({loading: true})}
style={style}/>
</div>
)
}
}
export default Biografy
Try this:
{ !this.state.photo
? (<i className="fa fa-spinner"></i>)
: (<img src={this.state.photo && `/images/${this.state.photo}`} alt="" />)
}
That means if there is photo, you show the image. If there is not, you show the loader.
Wrap it with suspense tags,
this is exactly what suspense is for;
As per the tutorial I read:
With the SVG and JS way, the JS to parse our HTML and add the SVG may fire before React has time to mount it's components. So we have to find a way to parse the HTML once React has mounted its components.
You will need to install the 'react-fontawesome' library as well as the 'free-solid-svg-icons' library from fontawesome
npm i --save #fortawesome/react-fontawesome
npm i --save #fortawesome/free-solid-svg-icons
Then you can insert the following code
import React, { Component, Suspense } from 'react'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faSpinner } from '#fortawesome/free-solid-svg-icons';
var data = require('./db.json');
class Biografy extends Component {
constructor(props) {
super(props)
this.state = {
photo: ""
}
}
componentDidMount() {
this.setState({
photo: data.biography[0].photo
})
}
render() {
return (
<Suspense fallback={<FontAwesomeIcon icon={faSpinner} size="2x" spin />}>
<div>
<img src={this.state.photo && `/images/${this.state.photo}`} alt="" />
</div>
</Suspense>
)
}
}
export default Biografy
Once you do this, you can delete the library import from your html and selectively choose the icon that you need
See the following documentation/tutorials for more info:
https://scotch.io/tutorials/using-font-awesome-5-with-react
https://github.com/FortAwesome/react-fontawesome

Rendering a canvas object received from props

Good day!
I am new to React and html2canvas. I am making an app which will take "screenshots" of my DOM using html2canvas then store it to an array of screenshots which will then be also rendered on the screen.
I am storing each <canvas> object received from the html2canvas promise to an array then pass it to my ScreenshotsContainer component which passes the array to the Screenshots component. The Screenshots component will then map the array of <canvas> objects to individual Screenshot components.
In App.js, I am calling the html2canvas function then pass the array to ScreenshotsContainer component
import React, { Component } from 'react';
import ScreenshotsContainer from './containers/ScreenshotsContainer/ScreenshotsContainer'
import html2canvas from 'html2canvas';
import './App.css';
class App extends Component {
state = {
canvasArray: []
}
getScreenshotHandler = () => {
console.log("[Canvas Array from state length:]" + this.state.canvasArray.length)
let canvasArray = this.state.canvasArray;
html2canvas(document.body).then((canvas) => {
canvasArray.push(canvas)
});
console.log("[Canvas Object value: ]" + canvasArray);
this.setState({ canvasArray: canvasArray })
}
render() {
return (
<React.Fragment>
<button onClick={this.getScreenshotHandler}>Get Screenshot</button>
<ScreenshotsContainer canvasArray={this.state.canvasArray} />
</React.Fragment>
);
}
}
export default App;
The ScreenshotsContainer component will pass the received array to the Screenshots component:
import React, { Component } from 'react';
import './ScreenshotsContainer.css'
import Screenshots from '../../components/Screenshots/Screenshots';
class ScreenshotsContainer extends Component {
render() {
return (
<div className="ScreenshotsContainer">
<Screenshots canvasArray={this.props.canvasArray} />
</div>
);
}
}
export default ScreenshotsContainer;
The Screenshots component will map the array and pass each canvas object to the Screenshot component:
import React, { Component } from 'react';
import Screenshot from './Screenshot/Screenshot';
class Screenshots extends Component {
render() {
const screenshots = this.props.canvasArray.map(canvas => {
return (
<Screenshot
key={Math.random}
canvasObj={canvas}
/>
)
})
return (
<React.Fragment>
{screenshots}
</React.Fragment>
);
}
}
export default Screenshots;
Here is the Screenshot component
import React from 'react';
import './Screenshot.css';
const screenshot = (props) => (
<div className="Screenshot" >
<canvas ref={props.canvasObj} style={{
width: '10%',
height: '10%'
}} />
</div>
);
export default screenshot;
What I actually get when pressing the button:
Actual screenshot of my result
I was wondering which part went wrong. Any help would be appreciated.
This particular library works in a specific way (looks like it's doing a lot of "magic" under the hood - you should look at the source code here more specifically the renderer folder inside src)
Saving the canvas to the state inside of an array (the correct react way of doing things) will be a problem as it saves it as a complex object with many methods etc... and we can not render objects... This lib was not written with React in mind...
The code sample below is a simple implementation in React...
Here is a live demo: https://codesandbox.io/s/9y24vwn1py
import React, { Component } from 'react';
import html2canvas from 'html2canvas';
class App extends Component {
constructor(props) {
super(props);
this.captureRef = React.createRef();
this.displayRef = React.createRef();
}
getScreenshotHandler = () => {
html2canvas(this.captureRef.current).then(canvas =>
this.displayRef.current.appendChild(canvas),
);
};
render() {
return (
<div>
<div ref={this.captureRef}>
<h2>This enitre div will be captured and added to the screen</h2>
</div>
<button onClick={this.getScreenshotHandler}>Get Screenshot!</button>
<section>
<h5>Your screenshots will be availbale below</h5>
<div ref={this.displayRef} />
</section>
</div>
);
}
}
export default App;
EDIT: based on the comment below here is yet another workaround:
class App extends Component {
constructor(props) {
super(props);
this.state = { canvasArray: [] };
this.captureRef = React.createRef();
}
getScreenshotHandler = () => {
html2canvas(this.captureRef.current).then(canvas =>
this.setState({
canvasArray: [canvas.toDataURL(), ...this.state.canvasArray],
}),
);
};
renderCanvas = () => {
return this.state.canvasArray.map((canvas, i) => {
return <img key={i} src={canvas} alt="screenshot" />;
});
};
render() {
return (
<div className="wrapper">
<div ref={this.captureRef}>
<p>This enitre div will be captured</p>
</div>
<button onClick={this.getScreenshotHandler}>Get Screenshot!</button>
<section>
<h5>Your screenshots will be availbale below:</h5>
{this.renderCanvas()}
</section>
</div>
);
}
}
Link to live demo: https://codesandbox.io/s/1r213057vq

Categories

Resources