Constant Rerender when downloading an image (React) - reactjs

I am trying to download an image from AWS, display it in my React component, and then on a button click, load a new image. The problem I am facing is that every time I go to load a new image, the page loads image after image, and won't stop rerendering with new images.
Here is my code for the landing page:
import React from 'react';
import Background from './Media/marmaduke2.jpg';
import Comic from'./Comic.js';
import { useState, useEffect } from 'react';
export default function RandomarmLanding() {
const [score, setScore] = useState(0)
const [isReal, setIsReal] = useState(false)
const [caption, setCaption] = useState('')
async function getComics() {
const response = await fetch('http://127.0.0.1:5000/comics')
const data = await response.json()
setCaption(data['caption'])
setIsReal(data['is_real'])
}
function GuessYes() {
if (isReal == true) {
setScore(score + 1)
}
}
function GuessNo() {
if (isReal == false) {
setScore(score + 1)
}
}
useEffect(() => {
getComics()
},[])
return(
<div style={{backgroundImage: `url(${Background})`}}>
<Comic caption={caption}/>
<button onClick={GuessYes}>Yes</button>
<button onClick={GuessNo}>No</button>
<button onClick={getComics}>get comics</button>
<h2>{score}</h2>
</div>
)
}
Here is my code for the Comic component:
import React from 'react';
import comic from './Media/comic.png';
export default function Comic (props) {
return(
<div>
<img src={comic}/>
<h1>{props.caption}</h1>
</div>
)
}
Here is the code that is actually downloading the image:
import boto3
def download_comic(num):
s3 = boto3.resource('s3')
s3_client = boto3.client('s3')
bucket = s3.Bucket('bucket_name')
s3_client.download_file('bucket_name',f'{num}.png', '/path/my-app/src/components/Randomarm/Media/comic.png')
Any thoughts on what might remedy this?
Thanks for the help!
EDIT
Instead of downloading the image to display it, I am just showing the image from my s3 bucket, using <img src=bucketimage.gif'/>. This way I avoid the constant rerendering all together.

I believe it's because of the state being changed in useEffect inside the method getComics where you are setting the caption.

I solved it. Instead of downloading the image, I am just displaying the image from my s3 bucket. That way I avoid downloading the image all together.

Related

How to handle missing image in react 17?

I'm getting users data from an api which the profile pictures path could be invalid, so i was able to cover the part of using a default image, but the 404 error of the missing picture is still exists ... i wonder how can i prevent it. I went over ALL the solutions and still couldn't find a good one.
Here is my component that renders the image
import React, { useState } from 'react';
const Image = props => {
const { image } = props;
const [imageLoading, setImageLoading] = useState(true);
const imageLoading_Error = () => {
setImageLoading(false)
}
return (
<div item>
<img src={imageLoading
?
`https://myusers.com/user/${image}` // don't mind the address
:
`${window.location.origin}/assets/default.png`}
onError={() => imageLoading_Error()}
/>
</div>
)
}
export default Image;

Can't call setState on a component that is not yet mounted. - React

I am creating a React component using an npm wysiwyg. When the page loads, I grab some user data from a context I have set up. I grab the user's name and cover letter which is some html code. When the page loads I want to change the wysiwyg's state to contain the cover letter that way it displays it to the user. It works the first time the page loads, however if I hit refresh or try to open the page again, the contents of the wysiwyg disappear. Upon inspection of the console, I am met with a
Warning: Can't call `setState` on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to `this.state` directly or define a `state = {};` class property with the desired state in the n component.
I am unsure of what I am doing wrong.
Here is my component:
import React, { useContext, useEffect, useState } from 'react';
import { LoggedInContext } from './contexts/LoggedIn';
import { Editor } from 'react-draft-wysiwyg';
import { EditorState, ContentState } from 'draft-js';
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css';
import htmlToDraft from 'html-to-draftjs';
import './Profile.css';
const ProfileSettings = () => {
const { user, coverLetter } = useContext(LoggedInContext);
const blocksFromHtml = htmlToDraft(coverLetter);
const { contentBlocks, entityMap } = blocksFromHtml;
const contentState = ContentState.createFromBlockArray(
contentBlocks,
entityMap
);
const editorState = EditorState.createWithContent(contentState);
const [editorStateReact, setEditorStateReact] = useState(editorState);
const onEditorStateChange = (editorState) => {
setEditorStateReact(editorState);
};
return (
<div className="Profile">
<h2>User: {user}</h2>
<Editor
editorState={editorStateReact}
toolbarClassName="toolbarClassName"
wrapperClassName="wrapperClassName"
editorClassName="editorClassName"
onEditorStateChange={onEditorStateChange}
/>
<button className="btn btn-lg btn-primary my-3">Save Profile</button>
</div>
);
};
export default ProfileSettings;

getting image from local storage using react

I am trying to work on a simple image editor using cropperjs and react, I want my project to let the user upload an image crop it and then download it I am using the browser local storage to store the user's image then I am importing the image from local storage to the canvas, if the user does not upload an image I have a default image to let the user play with.
The local storage is storing the user image properly and download is also working for me but my problem is that the local storage image is not coming to canvas and it shows only the default image.
here is my upload form component code
import React from 'react';
const UploadForm = ({uploader}) => {
const handleChange = (e) =>{
uploader(e.target.files[0])
}
return <form>
<input type="file" accept ="image/*" onChange ={handleChange}/>
</form>;
}
export default UploadForm;
and here is my App component code
import React, { useState, useEffect } from 'react';
import Cropper from './comps/cropper.jsx';
import UploadForm from './comps/UploadForm.jsx';
const App = () => {
const [url, setUrl] =useState('');
const uploader = (file) =>{
const reader = new FileReader();
reader.addEventListener('load', ()=>{
localStorage.setItem('recent-image',reader.result)
})
reader.readAsDataURL(file);
}
useEffect(() => {
setUrl(localStorage.getItem('recent-image'));
}, [])
return (
<div>
<UploadForm uploader = {uploader}/>
<Cropper src ={url}/>
</div>
);
}
export default App;
and here is my cropper component code
import React, { Component } from 'react'
import Cropper from 'cropperjs';
import 'cropperjs/dist/cropper.min.css';
import './cropper.css'
import image from '../image.png';
export class cropper extends Component {
state= {
imageDestination:""
}
imageElement = React.createRef();
componentDidMount(){
const cropper = new Cropper(this.imageElement.current, {
// zoomable:true,
scalable:true,
aspectRatio:1,
crop:()=>{
const canvas = cropper.getCroppedCanvas();
this.setState({
imageDestination:canvas.toDataURL('image/png')
})
}
});
}
render() {
return (
<div>
<div className ="img-container">
<img ref ={this.imageElement} src ={this.props.src||image} alt ="source"/>
</div>
<img src = {this.state.imageDestination} className = "img-preview" alt ="Destination" />
{this.state.imageDestination&&<a href ={this.state.imageDestination}
download>Download</a>
}
</div>
)
}
}
export default cropper
in cropper.jsx, you export the class cropper and also export default cropper in the end of the file too.
export class cropper extends Component
export default cropper
Maybe that's what is causing the issue

Implement Draft JS into Next JS, Text Editor Won't Show

I am trying to implement draft js as text editor into my next.js project. I have implemented all code based on official guide but the text editor won't show. Here is my code
index.js
import React, { Component } from 'react'
import { useRouter, Router } from 'next/router';
import Layout from '../../components/MyLayout';
import Settings from '../../components/Settings';
import MyEditor from '../../components/TextEditor';
import fetch from 'isomorphic-unfetch';
import {Editor, EditorState} from 'draft-js';
const Post = (props) => {
const router = useRouter();
const object = props.post.data[0];
return (
<Layout>
<h1>{object.title}</h1>
<div className="p-2 border bg-light text-right text-dark">Oleh: {object.username}</div>
<MyEditor/>
</Layout>
);
}
Post.getInitialProps = async function(context) {
const {id} = context.query;
const FormData = new URLSearchParams();
FormData.append('slug',`${id}`);
const res = await fetch(Settings.api+'viewPost',{
'method': 'POST',
'body': FormData
});
const post = await res.json();
console.log('aw');
return {
post
};
};
export default Post;
TextEditor.js
import React from 'react';
import ReactDOM from 'react-dom';
import {Editor, EditorState} from 'draft-js';
export default function MyEditor() {
const [editorState, setEditorState] = React.useState(
EditorState.createEmpty()
);
return (
<Editor
editorState={editorState}
onChange={setEditorState}
/>
);
}
I really appreciate any answer. Thank you
It looks like you're doing the right thing - Draft-js is very much a low-level editing tool that you can build rich text-editing on top of - I'm guessing that the editor is actually rendering on the page, but you haven't added any toolbars or complex initial state so you're just seeing a blank page.
I reproduced a super basic Next JS example here: https://codesandbox.io/s/naughty-swirles-7nmbf?file=/pages/index.js and you'll see that the editor itself is 'invisible' as there is no styling applied and no toolbar. If you look further into the Draft-js docs you'll see you can apply initial config objects that will give it a basic style (which you can further customize as you wish).

Import image dynamically in React component

I have an Articles component that shows a blog page with listed articles.
render() {
const articles = {
...this.state.articles
}
const article = Object.keys(articles).map(cur => {
return <Article
key={this.state.articles[cur].id}
imgName={this.state.articles[cur].thumb}
title={this.state.articles[cur].title}
meta={this.state.articles[cur].meta}
clicked={() => this.detailedHandler(this.state.articles[cur].id)}
detailed={this.state.articles[cur].detailed} />
});
As you can see I pass image name with props to Article component.
I want then to display the appropriate image for each article.
How do I import an image in Article component based on the props I receive (props.imgName) from Articles component?
I used context.
const images = require.context('../../../assets/img', true);
loadImage = imageName => (assets(`./${imageName}`).default);
<img src={loadImage("someimage.png")} alt="" />
I don't know if this is an optimal solution, but it works.
You can load images dynamically from the API response with dynamic imports that is Stage 3 proposal as of now.
The resulting code should look something like:
loadImage = imageName => {
import(`./assets/${imageName}.jpg`).then(image => {
this.setState({
image
});
});
};
render() {
const { image } = this.state;
return (
<Fragment>
{image && <img src={image} alt="" />}
</Fragment>
);
}
View Codesandbox Demo
This feature is supported out of the box in create-react-app, If using other systems, you can use the Babel plugin
For anyone looking for a modern approach using async-await and custom react hooks, I found a pretty slick solution. Create a file called useImage.js and paste the following code:
import { useEffect, useState } from 'react'
const useImage = (fileName) => {
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const [image, setImage] = useState(null)
useEffect(() => {
const fetchImage = async () => {
try {
const response = await import(`../assets/img/${fileName}`) // change relative path to suit your needs
setImage(response.default)
} catch (err) {
setError(err)
} finally {
setLoading(false)
}
}
fetchImage()
}, [fileName])
return {
loading,
error,
image,
}
}
export default useImage
Then just import the custom hook into your image component, mine looks something like this:
import useImage from '../../hooks/useImage'
import Typography from './Typography' // simple plain-text react component
const Image = ({ fileName, alt, className, ...rest }) => {
const { loading, error, image } = useImage(fileName)
if (error) return <Typography>{alt}</Typography>
return (
<>
{loading ? (
<Typography>loading</Typography>
) : (
<img
className={`Image${
className
? className.padStart(className.length + 1)
: ''
}`}
src={image}
alt={alt}
{...rest}
/>
)}
</>
)
}
export default Image
The nice thing about this solution is that no matter where your component is in relation to your assets folder, the react hook is always in the same place, so the relative path stays the same.
there are my way, works nicely:)
import React, {useState} from "react"
const getFavorites = (props) => {
const {item, favouritesNote} = props;
const [image, setImage] = useState("");
(function (imageName) {
import(
`../../../../assets/images/chart/favorite${imageName ? "_active" : ""}.png`
).then((image) => setImage(image.default));
})(favouritesNote[item.id]);
return (
<div>{image && <img alt="" className="img-responsive" src={image} />}</div
)
}
Note: All these comments work complementary with the #jadenRose hook solution which is a great abstraction.
You can import the images dynamically with the native js dynamic import('') , there's no need for React Lazy.
However, you have to be aware of the path/content you pass to the import(path) because depending on how you are planning to include the images to import in the final bundle there could be restrictions. There are two main ways:
Note: This is for Rollup, in Webpack i didn't try it but should be similar
a)- Making rollup automatically include the possibles files to import while creating the bundle using the official plugin https://www.npmjs.com/package/#rollup/plugin-dynamic-import-vars please read the doc and notice how there are important restrictions in the 'path' string, essentially you should just set the file name in the variable, the rest of the path have to be fixed in the import('') in order to provide rollup a restricted scope to import. eg:
OK
import(../assets/img/${fileName}.svg)
Wrong
import(filePath)
b)- Include in the bundle programmatically the files you can dynamically import example
//rollup.config.js
import copy from 'rollup-plugin-copy';
plugins: [
copy({
targets: [ { src: 'src/assets/icons/*', dest: 'lib/assets/icons' },],
}),
…
],
With this option b) you have no restrictions on the variable content but have to be careful with what you included in the bundle.
Conclusion: You can use dynamic import(...) but if you not properly handle the files inclusion on the bundle, it can be possible they are excluded and then the dynamic import will fail in the consumer.
I found this worked best for me:
I created an index file inside the images folder. there I imported all the images I have and created a class component with the variables assigned to each image import. this is because when we import an image in react using the import statement, that is, import someImage from './somewhere' react assigns the 'someImage' variable to a module with a 'static media' address, my terminology there might be wrong. Here is the example:
import image13_1 from './image13_1.png';
import image13_2 from './image13_2.png';
import image13_3 from './image13_3.png';
import image13_4 from './image13_4.png';
import image13_5 from './image13_5.png';
import image13_6 from './image13_6.png';
import image13_7 from './image13_7.png';
export class IMG{
1= image13_1
2 = image13_2
3 = image13_3
4 = image13_4
5 = image13_5
6 = image13_6
7 = image13_7
}
export default IMG;
from here I just import the IMG class and create an instance of it and call the image number a property:
var img = new IMG()
console.log('img',img[1]

Resources