Import image dynamically in React component - reactjs

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]

Related

react dynamic component type example

How/Where can i add types to the dynamic component Icons[name] ?
import * as Icons from "react-icons/fa";
const DynamicFaIcon = ({ name }: any) => {
const IconComponent = Icons[name];
return <IconComponent />;
};
You could just grap the keys from the import, since its a JS object like any other:
import * as Icons from "react-icons/fa";
const DynamicFaIcon = ({ name }: {name: keyof typeof Icons}) => {
const IconComponent = Icons[name];
return <IconComponent />;
};
I would be careful about importing literally everything from that package though. There's over 1,500 components in there, does any application actually make use of all of them? You'll end up bundling way more than you need.
This is my answer on dynamic icons problem, is it your question answer ?? , maybe it will help you, use it like this `
import React from 'react';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import {library} from '#fortawesome/fontawesome-svg-core';
import * as Icons from '#fortawesome/free-solid-svg-icons';
const iconList = Object.keys(Icons)
.filter((key) => key !== 'fas' && key !== 'prefix')
.map((icon) => Icons[icon]);
library.add(...iconList);
const Feature = ({ carFeature }) => {
console.log(carFeature);
return (
<div>
<FontAwesomeIcon icon={carFeature?.icon} color="#ddd" />
<h3>{carFeature?.name}</h3>
<p>{carFeature?.desc}</p>
</div>
);
};
export default Feature;
If no then try this
Not the OP's direct issue but for users encountering this error for libraries not under their control, one can suppress this error is by adding:
{
...
"suppressImplicitAnyIndexErrors": true,
...
}
to the tsconfig.json file.
Read more here Question Answer

react-img-mapper blanck screen

I have a project where i need to map specific areas of an image, for that i'm trying to use react-img-mapper, but when i try to run the example code, the only think that shows is a blanck screen, react do not log any erros. What am i doing wrong ?
import React from 'react';
import ImageMapper from 'react-img-mapper';
const Mapper = props => {
const URL = 'https://raw.githubusercontent.com/img-mapper/react-docs/master/src/assets/example.jpg';
const MAP = {
name: 'my-map',
// GET JSON FROM BELOW URL AS AN EXAMPLE
areas: 'https://raw.githubusercontent.com/img-mapper/react-docs/master/src/assets/example.json',
};
return <ImageMapper src={URL} map={MAP} />
}
export default Mapper;

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;

Constant Rerender when downloading an image (React)

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.

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).

Resources