Reactjs: why img's src cannot be passed to props? [duplicate] - reactjs

This question already has an answer here:
react dynamic import using a variable doesn't work
(1 answer)
Closed 2 years ago.
I am passing a prop (which contains a string link) into a component. This props is then used inside the src property of <img /> but this causes a broken image instead. What is the correct way of doing this without using the import...from... method in the beginning of my component. The code below shows other alternatives that i tried which dont work.
class Entry extends React.Component {
render() {
const link = '../../images/company-logo.png';
const image = require(link); //error: cannot find module
const imagee = require('../../images/company-logo.png'); //works fine, but not ideal
return (
<div className="entry">
<img src={this.props.imageLink}/> //results in a broken image
<img src={link}/> //results in a broken image
<img src={imagee}/> //works fine
</div>
);
}
}

You need to import the image first and then either use it directly as value of src attribute on img element or pass it to some other component as a prop
import myImg from '../../images/company-logo.png';
now either use myImg directly as a value of src attribute
<img src={myImg}/>
or pass it down as a prop
<div className="entry">
<SomeComponent img={myImg} />
</div>

Actually you can pass image src via props but some where you have to import it. You have to do that because React projects mostly use Webpack to build the code and the images also be built with JS code.
I know there are 2 options which might help you solve this problem.
Import image from Parent Component and pass it to the child one:
import Image from '../images/image-01.png';
function ParentComponent {
return <ChildComponent img={Image} />;
}
function ChildComponent(props) {
return <img src={props.img} />;
}
Import all images inside a folder and use it as an image dictionary by using require.context:
function importAll(r) {
return r.keys().map(r);
}
const images = importAll(require.context('../images', false, /\.(png|jpe?g|svg)$/));
function ChildComponent(props) {
const link = 'image-01.png'; // or link = props.parentLink;
return <img src={images[link]} />;
}
You could find more information about option 2 in this answer.
Hope it could help you solve your problem.

Related

React: Client & Server way to render children to string

How can I render the children as a string in React 18/Next 13?
React says renderToString is not suggested on the client, and it's not clear to me how to render it on the server.
The documentation here gives an example but it's not clear how it works in an actual react component as I get errors that I cannot create another node if the previous one wasn't removed from the head.
import { createRoot } from 'react-dom/client';
import { flushSync } from 'react-dom';
const div = document.createElement('div');
const root = createRoot(div);
flushSync(() => {
root.render(<MyIcon />);
});
console.log(div.innerHTML); // For example, "<svg>...</svg>"
Source
Whether I get the data on the server or client side, just looking for a working example either or.
function ExampleChildComponent() {
return (
<div className="bg-green-500 w-20 h-20">
Hello I am a green box
<button className="bg-blue-100 px-6 py-3">I am a button</button>
</div>
)
}
function LogChild({ children }: any) {
// How do you get the children to a string?
console.log(someFn(children));
// Interested in both an output showing <GreenBox />
// and/or the parsed version which shows the stuff inside GreenBox
return (
<p>Logged!</p>
)
}
function App(){
return (
<LogChild>
<ExampleChildComponent />
</LogChild>
)
}
Alternatively, if there's an open source project that I can just study works too. Google is very sparse in examples for this question. Either that or answers are pre React 18.

Only first element in a react.createElement is rendered

I need to render dynamicaly an img. Here is my function that translate my logos to components :
import {ReactComponent as MyLogo1} from '../assets/img/logo1.svg'
import {ReactComponent as MyLogo2} from '../assets/img/logo2.svg'
import {ReactComponent as MyLogo3} from '../assets/img/logo3.svg'
export const logoToComponent: any = {
logo_1: MyLogo1,
logo_2: MyLogo2,
logo_3: MyLogo3
};
and then I render it like this :
[...]
const logo: any = (key: string, props: any) => {
return logoToComponent[key] ? React.createElement(logoToComponent[key], props) : null;
};
return (
[...]
{logo(`logo_${category.code}`, { className: "mx-auto mb-3 w-6 h-6" })}
[...]
)
The problem is, only the first logo is rendered. In my DOM, when I inspect, every logos are there but only the first one is visible. And in my SVGs, the fill is set for each logos.
Any idea why React only render the first element ?
PS: If I delete the first line in my logoToComponent function, the second is visible and not the following ones.
I post the answer in case it can help.
Every SVG had the same id "clip-path". I just set a unique id in each SVG, and it works fine.

React How to pass arguments to function

Hi I found a question asking the same thing but they coded completely different using 'class name extends', I am just using 'function name'. I was wondering how I would I solve this problem or do I have to rewrite my program.
I have styles at the bottom I left off.
Window.js
import React from 'react';
import "../css/Start.css";
export default function Window(nuts) {
let ulList=[]
for (let i=0;i<nuts.file.length;i++) {
ulList.push(<li>
{
nuts.file[i]
}
</li>)
}
let imageList=[]
for (let i=0;i<nuts.image.length;i++) {
imageList.push(<img src={nuts.image[i]} alt={nuts.image[i]}/>)
}
return (
<div style={main}>
<p>{nuts.name}</p>
<p>{nuts.date}</p>
<p>{nuts.note}</p>
{ulList}
{imageList}
<button> Demo </button>
</div>
);
}
Project.js
import React from 'react';
import Background from '../images/projectbackground.jpg';
import "../css/Start.css";
import Window from './Window'
export default function Project() {
const files = ['f1','f2','f3']
const images = ['p1','p2','p3']
const nuts = {name:'phil',date:'2/2/16',note:'this is a note',file:files,image:images}
return (
<div style={main}>
<Window nuts={nuts}/>
<div style={footer}>
</div>
</div>
);
}
Your function component will get passed all the properties together in an object.
There are three changes you could make to have this work:
render <Window {...{nuts}} /> instead (not recommended but is an option)
change parameters to function Window(props) and all your other code to say props.nuts instead of just nuts
change parameters to function Window({nuts}) to destructure the (no longer named) "props" object
nuts is being passed to Window via the props object.
You either need to destructure nuts in-line or in your function body.
function Window({ nuts })
or
function Window(props) {
const { nuts } = props;
}

Replace a string with Component in ReactJS

I'm trying to replace a string with a React Component but it's not working. It's returning [object Object].
I've tried to use renderToString and renderToStaticMarkup from react-dom/server to render the component, but no success.
import React from 'react';
const MyComponent = ({ children }) => <strong>{children}</strong>;
function App() {
const content = 'Hi #user'.replace('user', <MyComponent>user</MyComponent>);
return <div className="App" dangerouslySetInnerHTML={{ __html: content }} />;
}
export default App;
Expected result:
Hi #<strong>user</strong>
Actual result:
Hi #[object Object]
To render components, they need to return those components as part of your rendering. This is typically done with jsx tags, which get transformed into calls to React.createElement, which in turn creates objects that instruct react what to do.
But with string.replace, you're only going to produce a string, not react elements, so react has nothing to work with other than that string. And as for dangerouslySetInnerHtml, that will only work if you have actual dom content you want to insert, not react components (plus, it's dangerous).
Most of the time when you're rendering components of an unknown quantity this is done by having an array of data, which you then map to the components. For example:
function App() {
const greetings = ['Hi #', 'Aloha #'];
const content = greetings.map(greeting => (
<React.Fragment>
{greeting}
<MyComponent>user</MyComponent>
</React.Fragment>
));
return <div>{content}</div>;
}
Taking in a string and trying to interrogate that string is rather unusual, but if that's what you need to do, then you'll probably want to do something like this:
function App() {
const str = 'Hi #userAloha #user';
const greetings = str.split('user');
greetings.pop() // remove an empty string from the end of the array
const content = greetings.map(greeting => (
<React.Fragment>
{greeting}
<MyComponent>user</MyComponent>
</React.Fragment>
));
return <div>{content}</div>
}
Note that this is basically identical to my first code, except there's an extra step to turn the string input into an array.

React Component with JSON Image path

I have a react component that is pulling in some data from a local json file. That data has some links to images. However the images aren't loading even though the paths are correct. I was using require before but in a different code setup. Now that I've transitioned it to this the images won't load. i'm using webpack 4 so i'm guessing i need to require the images again? How do i do that in the below statement ?
the part that is failing is the 'image={release.imageURL}' which is being passed to the 'src' of the component
import React from "react";
import ReactDOM from "react-dom";
import Release from "./release.js"
import data from "../data/releases.json"
const allReleases = data.map(release => {
return (
<div className="column-2">
<Release
key={release.id}
url={release.releaseURL}
image={release.imageURL}
cta={release.cta}
title={release.title}
artists={release.artists}
/>
</div>
)
})
const Releases = () => {
return (
<div>
<div className="row">
<h3>All Releases</h3>
{allReleases}
</div>
</div>
)
}
export default Releases;
Without knowing the details of what you're trying to achieve, I think you can do the following:
Because you're providing paths in your json, you'll need to require them dynamically.
releases.json
[
{
imageURL: "/image01.jpg"
},
{
imageURL: "/image02.jpg"
},
{
imageURL: "/image03.jpg"
}
]
then assuming you know the relative path to these images you can require them inside your map
const allReleases = data.map(release => {
return (
<div className="column-2">
<Release
url={require('./path/to/images' + release.imageURL)}
/>
</div>
)
})
Alternatively, you could make your releases.json a js file and import your images there and put them in your data as variables.
Bro, I was working on this problem and eventually figure it out:
1- All you have to do is changing the path of the image from your JSON file. That means the path should be related to your react component (allReleases).
2- Next thing, instead of using
*
<Release
key={release.id}
url={release.releaseURL}
image={release.imageURL} // ==> WRONG
cta={release.cta}
title={release.title}
artists={release.artists}
/>
Just go with:
*
<Release
key={release.id}
url={release.releaseURL}
image={`${release.imageURL}`} // ===> CORRECT
cta={release.cta}
title={release.title}
artists={release.artists}
/>

Resources