Only first element in a react.createElement is rendered - reactjs

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.

Related

Inject Props to React Component

For security reasons, I have to update ant design in my codebase from version 3 to 4.
Previously, this is how I use the icon:
import { Icon } from 'antd';
const Demo = () => (
<div>
<Icon type="smile" />
</div>
);
Since my codebase is relatively big and every single page use Icon, I made a global function getIcon(type) that returns <Icon type={type}>, and I just have to call it whenever I need an Icon.
But starting from antd 4, we have to import Icon we want to use like this:
import { SmileOutlined } from '#ant-design/icons';
const Demo = () => (
<div>
<SmileOutlined />
</div>
);
And yes! Now my getIcon() is not working, I can't pass the type parameter directly.
I tried to import every icon I need and put them inside an object, and call them when I need them. Here's the code:
import {
QuestionCircleTwoTone,
DeleteOutlined,
EditTwoTone
} from '#ant-design/icons';
let icons = {
'notFound': <QuestionCircleTwoTone/>,
'edit': <EditTwoTone/>,
'delete': <DeleteOutlined/>,
}
export const getIcon = (
someParam: any
) => {
let icon = icons[type] !== undefined ? icons[type] : icons['notFound'];
return (
icon
);
};
My problem is: I want to put someParam to the Icon Component, how can I do that?
Or, is there any proper solution to solve my problem?
Thanks~
You can pass props as follows in the icons Object:
let icons = {
'notFound':(props:any)=> <QuestionCircleTwoTone {...props}/>,
'edit': (props:any)=><EditTwoTone {...props}/>,
'delete':(props:any)=> <DeleteOutlined {...props}/>,
}
And then if you will pass any prop to the Icon component then it will pass the prop to the specific icon component
let Icon = icons[type] !== undefined ? icons[type] : icons['notFound'];
return (<Icon someParam={'c'}/>)

React event.target setState. Set list item backgroundColor

After clicking on the item list I want to change its background color. After clicking on another, I want the color to return to the default. I did something like that:
import React, {useState} from 'react'
function Node({expanded, name}) {
const [targetS, setTargetS] = useState()
const Select = (element) => {
const {target} = element
targetS && targetS.backgroundColor = ''
setTargetS(target)
targetS.style.backgroundColor = 'orange'
}
return (
<li onClick={Select}>
{expanded? '-':'+'} {name}
</li>
)
}
export default Node
but it doesn't work as I think
If you're using React, take advantage of writing JavaScript inside the JSX and React state. You don't need to manipulate the color of your element using event object directly. (The way you are doing it needs some correction also). You can let React be responsible for triggering the color change by leveraging react state to act as a toggle for your li className.
Create a activeNode state and its setter setActiveNode in parent component of Node. Then pass it as follows:-
<Node name={name} expanded={expanded} activeNode={activeNode} setActiveNode={setActiveNode}/>
import React, {useState} from 'react'
function Node({expanded, name, setActiveNode, activeNode}) {
const selectElement = (name) => {
setActiveNode(name)
}
return (
<li className={activeNode===name?"orange":""} onClick={()=>selectElement(name)}>
{expanded? '-':'+'} {name}
</li>
)
}
export default Node
Inside your stylesheet:-
orange{
background:"orange"
}

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

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.

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

How to update <img/> on prop change

Hi I am retrieving data from a Logitech Media server, title, artist, song etc and would like to know how to update the image returned by this server when the title of the song changes.
I have created a component that happily receives, displays, and updates all the relevant audio subcontrols , but I cannot get the song jpg to update on song title name change. The src of the image is set in state( and is the url of logitech media server needed to refreash the jpg)
The song title is happily updated when sent to the audioToolBar component
The title is defined in the switch statement and I thought by updating placing the songtitle in state and updating it when it changes would work, but whatever i try i get an error stating max depth of calls to setstate exceeded.
The Abbrievated (non functioning)code of component listed below
I am a newbie with react and js so any help greatly appreciated.
import React, { Component } from "react";
import { withStyles } from "#material-ui/core/styles";
import Grid from "#material-ui/core/Grid";
import SwitchedComponent from "./switchedComponent";
import AudioToolBar from "./audioToolBar";
class AudioLightandHeat extends Component {
state = {imgSrc :"http://192.168.1.1:9000/music/current/cover.jpg?player=Bathroom"};
getControls = ()=>{
for (let index = 0; index < this.audioControls.length; index++) {
const element = this.audioControls[index];
switch (element.name) {
case "Title":
this.titleCont = element
let songAndArtist = (this.titleCont.statesValue.textAndIcon).split("/")
this.song = songAndArtist[0]
this.setState({songTitle:this.song})
break;
case "Volume":
this.volumeCont = element;
break;
case "Mode":
this.modeCont = element;
break;
case "Track":
this.trackCont = element;
break;
case "Play Pause":
this.playCont = element;
break;
}
}
}
}
}
}
}
}
render() {
const { classes, theme } = this.props;
this.getControls()
return (
<>
{this.hasAudioControl ? (
<img src = {this.state.imgSrc} width={300} height={300} />
) : null}
</>
);
}
}
export default withStyles(styles, { withTheme: true })(AudioLightandHeat);
ok so I have refactored the entire component to look like this
import React, { Component } from "react"
import { withStyles } from "#material-ui/core/styles"
import Grid from "#material-ui/core/Grid"
import AudioToolBar from "./audioToolBar"
const styles = theme => ({
grid: {
flexGrow: 1,
},
});
class AudioLightandHeat extends Component {
render() {
const { classes, theme } = this.props;
return (
<>
{console.log(" in render")}
{this.props.componentProps ? (
<Grid container className={classes.grid}>
<img src = "http://192.168.1.1:9000/music/current/cover.jpg?player=Bathroom" width={300} height={300} />
</Grid>
) :null}
{this.props.componentProps ? (
<AudioToolBar title = {this.props.componentProps.audioLightHeatControls.titleCont}
volume={this.props.componentProps.audioLightHeatControls.volumeCont}
mode={this.props.componentProps.audioLightHeatControls.modeCont}
track={this.props.componentProps.audioLightHeatControls.trackCont}
playpause={this.props.componentProps.audioLightHeatControls.playCont}
reqStateChange={this.props.componentProps.reqStateChange}
/>
) :null}
</>
)
}
}
export default withStyles(styles, { withTheme: true })(AudioLightandHeat);
The title , wolume, mode, track all update correctly when the props are updated, but the img does. I suspect this is because the src url does not change and hence the browser does not ask for it again despite there being a new image on the server at the same addresss.
How can i force React to call for the image when in render.
I have tried adding date.now to end of the url i.e src = "http://192.168.1.1:9000/music/current/cover.jpg?player=Bathroom" + new Date().getTime() but then get a 404 error from the server, I also added the following to index.html
<meta http-equiv="cache-control" content = "max-age=0">
<meta http-equiv="cache-control" content = "no-cache">
<meta http-equiv="pragma" content = "node_modules">
but this does not work either.
Any help greatly appreciated.
More General Advice about your react component
All the state of your react components should be in the component state. (this.song, this.titleCont etc. are currently not part of the component state, but assigned to the instance of the component)
This this.setState({ songTitle: this.song }) seems better, but it is usually not a good idea to have calls to setState in a function which is called in the render method. (It is very easy to produce infinite loops like this...)
The specific reason why it is only rendered once
NOTE: I am guessing here. You should really refactor your component first. Also you should probably read into the react component life cycle a bit, before you continue. Maybe this can help you.
Currently your render method will be called once during the initial render. During this initial render this.getControls() will be called once, but will probably not trigger the "Title" case.
After this initial render the props and state don't seem to change and therefore the component wont rerender. Therefore the render method and this.getControls() are not called again and the setState call wont be done.

Resources