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
Related
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.
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.
So I've been having problems with this code that i am working on
Dependencies: React, Redux, Eslinter, PropTypes, BreadCrumb
There's a view page that imports various components from other files
The Existing Structure Goes:
import Component from './Component.js';
...
let var = '';
...
if (this.state.value !=== null) {
var = <Component/>
}
...
render() {
return(
<div>
SomeContent
{var}
</ div>
)
}
When i try to import my created PureComponent the code Compiles.
Component Dependencies: { Button, Modal }React-Bootstrap, React, PropTypes
However the page does not render, and i cant figure out the reason why it would not render when it is introduced in the same manner as the existing structure Above
Update: I have tried making a bare minimum component returning just a simple Div & got the same result
RESOLVED:
There was a depreciated reference in my component to Modal from 'react-bootstrap' which still contained the node_module reference title but not the corresponding JS file which they have since moved to 'react-modal'
import Component from './Component.js';
let entity = '';
if (this.state.value !== null) {
entity = <Component />
}
render() {
return (
<div>
SomeContent
{entity}
</ div>
)
}
Firstly, You can not use var keyword for naming a variable and also, please check your condition fulfillment.
When dealing with state and conditional rendering, you should put conditions within the render method (or in a class field):
Working example:
import React from "react";
import OtherComponent from "../OtherComponent";
class Example extends React.Component {
constructor() {
super();
this.state = {
value: ""
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({ value: event.target.value });
}
render() {
return (
<div>
SomeContent
{this.state.value && <OtherComponent />}
<br />
<input
value={this.state.value}
placeholder="Type something..."
onChange={this.handleChange}
/>
</div>
);
}
}
export default Example;
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
Im making my first react ptoject. Im new in JS, HTML, CSS and even web app programing.
What i try to do, is to display some infomration on button click.
I have an API, that looks like this:
endpoint: https://localhost:44344/api/Projects
My Data from it:
[{"id":1,"name":"Mini Jira","description":"Description for first project in list","tasks":null},{"id":2,"name":"Farm","description":"Description for second one","tasks":null}]
And im fine with that, i can get it easily by axios in my react app.
Now i will show you my Project.js Component:
import React, { Component } from "react";
import { ListGroupItem, Button, ButtonToolbar } from "react-bootstrap";
import ProjectDetails from "./ProjectDetails";
class Project extends Component {
render() {
return (
<ButtonToolbar>
<ListGroupItem>{this.props.project.name}</ListGroupItem>
<Button onClick={Here i want to display new component with details }bsStyle="primary">Details</Button>
</ButtonToolbar>
);
}
}
export default Project;
I have all data from api in project type.
My question is, how to display component that i named ProjectDetails.js on button click? I want to show all data stored in project from my api in separate view (new page or somethig like that).
View looks like this:
Thanks for any advices!
EDIT:
based on #Axnyff answer, i edited Project.js. it works ok. But when i want to (for testing) displat project.name, i get error map of undefined. My ProjectDetails.js:
import React, { Component } from "react";
class ProjectDetails extends Component {
state = {};
render() {
return <li>{this.props.project.name}</li>;
}
}
export default ProjectDetails;
EDIT2:
In Project.js in #Axnyff answet i just edited that line:
{this.state.showDetails && (
<ProjectDetails project={this.props.project} />
)}
i passed project by props, now it works like i want too. After click it displays project.name that i clicked on.
You should use state in your React component.
Let's create a field called showDetails in your state.
You can initialize it in your constructor with
constructor(props) {
super(props); // needed in javascript constructors
this.state = {
showDetails: false,
};
}
Then you need to modify the onClick to set that state to true
<Button onClick={() => this.setState({ showDetails : true })} bsStyle="primary">Details</Button>
And then use that state to show or not the ProjectDetails:
{ showDetails && <ProjectDetails /> }
The full component should look like
import React, { Component } from "react";
import { ListGroupItem, Button, ButtonToolbar } from "react-bootstrap";
import ProjectDetails from "./ProjectDetails";
class Project extends Component {
constructor(props) {
super(props); // needed in javascript constructors
this.state = {
showDetails: false,
};
}
render() {
return (
<ButtonToolbar>
<ListGroupItem>{this.props.project.name}</ListGroupItem>
<Button onClick={() => this.setState({ showDetails : true })} bsStyle="primary">Details</Button>
{ this.state.showDetails && <ProjectDetails /> }
</ButtonToolbar>
);
}
}
export default Project;
You can then modify the logic to add a toggling effect etc.
If you haven't done it, you should probably follow the official tutorial
function Bar() {
return <h1>I will be shown on click!</h1>;
}
class Foo extends React.Component {
constructor() {
super();
this.state = { showComponent: false };
}
handleClick = () => {
this.setState({ showComponent: !this.state.showComponent });
};
render() {
return (
<div>
{this.state.showComponent && <Bar />}
<button onClick={this.handleClick}>click</button>
</div>
);
}
}
ReactDOM.render(<Foo />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>