React rendering SVG overwrites other SVGs on the page - reactjs

Using babel-plugin-inline-react-svg from within my next.js app, I'm importing some SVGs into my React v16.0.0 component like so.
import React from 'react';
import Close from './close.svg';
import Chevron from './right.svg';
import EmptyCart from './empty.svg';
const Component = props => (
<div>
<Close />
<EmptyCart />
<Chevron />
</div>
);
When I run that code, the page is rendered with the 3 SVGs all being the same, like this:
Whichever of the SVGs I render first seems to take over all of the other ones. If I put <EmptyCart /> first, they'll all be cart icons. But here's the real kicker: When I inspect the DOM, the SVGs seem to all be correct (they're all completely different from each other).
Anyone seen this before? How is this even possible for the DOM to say one thing but the browser to render another thing?

It would be helpful to see the other SVGs as well, but if they are similar and the id's match, then this is your problem.
<path id="4eeded6c-befb-41ba-a055-83a9e4ddc009" d="M3.632 3.182H1.091A1.09 1.09 0 0 1 1.09 1h3.322c.467 0 .883.297 1.033.74l4.096 12.046.036.134c.083.406.53.777.928.78l8.87.056c.39.002.831-.361.925-.816l1.552-6.017a1.09 1.09 0 1 1 2.112.545l-1.539 5.96c-.285 1.417-1.625 2.518-3.064 2.51l-8.869-.057c-1.408-.008-2.718-1.073-3.036-2.451L3.632 3.182zM9.272 23a2.191 2.191 0 0 1-2.181-2.201c0-1.216.977-2.2 2.182-2.2s2.181.984 2.181 2.2A2.191 2.191 0 0 1 9.273 23zm10.91 0A2.191 2.191 0 0 1 18 20.799c0-1.216.977-2.2 2.182-2.2s2.181.984 2.181 2.2A2.191 2.191 0 0 1 20.182 23z"/>
You can see that this id get's targetted and reused within the SVG itself here:
<use xlink:href="#4eeded6c-befb-41ba-a055-83a9e4ddc009"/>
This is a common problem, especially when exporting from apps like photoshop etc. To avoid conflicts when i use svg's I manually change all id's to ensure uniqueness.
If it helps, I've created a code-pen which goes into more examples of how to re-use svg's : https://codepen.io/peter-mouland/pen/JErvZY

You should to assign different id to each svg icon in your config file. Like this:
// SVG are imported as react components
{
test: /\.svg$/,
use: [
{
loader: 'babel-loader',
},
{
loader: 'react-svg-loader',
options: {
svgo: {
plugins: [
{
removeTitle: true,
},
{cleanupIDs: {
prefix: {
toString() {
this.counter = this.counter || 0;
return `id-${this.counter++}`;
}
}
}},
],
floatPrecision: 3,
},
},
},
],
include: paths.svg,
},

The issue can be related to the non-unique ids in SVGs.
It is common that svg generators can return content with the same Ids like <mask id="mask0" /> which then is referenced by <g mask="url(#mask0)"/>.
In case you have two different SVGs with the same mask id you will likely to have an issue with rendering two different icons.
The simplest solution is to specify a unique id for each <mask /> and then don't forget to update the reference in <g />.

I also had a similar issue because i exported images from Figma and i was using them on a project.
So each time i include the other SVG as a component it will override one of the SVG and show the first one.
After a careful check, i noticed that they were actually having the same id and the same image name
having the fill attribute point to the pattern45550 which is my new svg name in my case
<rect width="48" height="52" fill="url(#pattern45550)"/>
renaming the id to pattern45550
<pattern id="pattern45550" patternContentUnits="objectBoundingBox" width="1" height="1">
renaming the image name image10000000 also in the below tag
<image id="image10000000" width="2887" height="3162" xlink:href="data:image/png;base64...
then lastly point the URL to image10000000 image
<use xlink:href="#image10000000" transform="translate(0 -0.00550212) scale(0.00034638)"/>
and it all worked well in my case.

In my case, the conflict between both SVG was because internally they have the same .className
Two solutions:
Change the intern class name in one of them
If it is possible (for example if you are using CRA) load one of the svg using <img src'file.svg'>

In some Case we define styles of our SVG like this
<svg>
<defs>
<style>.a{fill:none;}</style>
</defs>
</svg>
here we define a class name style as .a, in my project all of svgs use the same class name and if I use more than one SVG in DOM then my svgs styles are overwrite and break the design
solution: you should change classname to avoid duplicated class name

I encountered this issue when I try to use multiple svgs downloaded from a figma file on a screen, the other svg override the other. The issue was the class name in each svg was similar. So I edited the class name to prevent them from clashing

The problem is most likely that the IDs are not unique between the SVGs, as has been mentioned above. There are some loaders which can handle this problem for you automatically, so that you don't have to manually change all of the IDs and references to them. Check this out: https://github.com/SilverFox70/svg-react-loader

Related

How to dynamically populate tailwindcss grid-cols-x property?

I am currently working on a page using NextJs and TailwindCss. The user has the ability of loading an image locally and setting the number of pieces horiz/vert (rows/cols) that they wish the image to be split in. To properly display this, I need to set the grid to the proper number of columns in the parent element.
I have an API I call locally that uses sharp to perform the split and calculate the width and height and I sort the images in my array so that they are in order since it is async. I then also generate a dynamic class string that just populates the proper number of columns for later assignment to my parent grid elements class.
CLASS STRING POPULATION
const gridClass = `grid grid-cols-${numCols} gap-2 pt-2`;
//*** At this point, the value of gridClass, if columns were set to 3, using template literals is :
'grid grid-cols-3 gap-2 pt-2'
//The proper string is now populated and passed back in the API response via classCss key
res.status(200).json({ msg: 'Success splitting', tileData: tiles, classCss: gridClass})
PAGE SNIPPET:
<div id="final">
<div className={tileCss} > //<--This is where I pull in the generated class string
{
imageData.map((nft, i)=>(
<div key={i} className='border shadow rounded-x1 overflow-hidden'>
<Image src={nft.imgSrc} alt="image" layout="responsive" width={nft.tileDimX} height={nft.tileDimY}/>
</div>
))
}
</div>
</div>
This sometimes works but other times it doesn't. Usually if I set the columns to 3, it seems to work properly, but if I set it to 5 lets say, regardless of the input image size, it just puts them all in a single column with large images. Oddly however, the parent grid class on the page is correct, it just seems that it isn't adhered to. I will provide some snapshots below to show what I'm talking about. I've been trying to figure this out for a couple days, however I haven't had luck and since I'm new to NextJs I thought I would share here and see if I'm just doing something stupid. Thanks!
The below results also don't seem to care if the viewing window is stretched wide or reduced in size. I just took the snapshots below so that you could see what was happening in a smaller viewing window.
This is the expected result where the image columns should match the columns entered by the user:
Notice how the css class shows up under styles as well:
This is the improper result, where the user selected 5 columns, the image was split into the correct number of columns, but the display of this in the front end grid does not follow the css.
As you can see grid-cols-5 is correct from a class standpoint, but the viewed result doesn't adhere to this.
Grid-cols-5 is in html class but missing under styles applied:
So I finally found the source to the issue. It seems that tailwindcss performs an optimization on classes brought in and if you are not using the class anywhere in your html, it doesn't include those classes. So I not only needed to return my dynamic string with the expected class, but I also needed to add all the classes I would possibly be using into my tailwind.config.js file under the safelist key.
So my config now looks like this and it is working as expected:
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx}',
],
safelist: [
{
pattern: /grid-cols-./,
}
],
theme: {
extend: {},
},
plugins: [],
}
More information can be found here:
https://tailwindcss.com/docs/content-configuration#using-regular-expressions
Thanks again Mohit for your assistance.
One of the problem I noticed is in grid-cols-${numCols} in the line
const gridClass = `grid grid-cols-${numCols} gap-2 pt-2`;
TailwindCSS doesn't allow you to generate classes dynamically. So when you use the following to generate the class… grid-cols-${numCols} as a string.
…TailwindCSS will not pick that up as a valid TailwindCSS class and therefore will not produce the necessary CSS.
You can use the function from where you are getting numCols and instead of returning the value of numCols, simply return grid-cols-${numCols}.
Suppose let say your function be getNumofCols(), then modify it like this
function getNumofCols() {
...
...
...
...
...
return "grid-cols-" + numCols ;
}
So that it returns the complete string .
And use it like again
const gridClass = `grid ${getNumofCols()} gap-2 pt-2`;
If your function uses any parameter then you can create a new function and call this function and just add grid-cols- to the return value.
By doing it this way, the entire string for every class is in your source code, so Tailwind will know to generate the applicable CSS.

React/Webpack 4: Advice needed, best practice for loading svgs selectively from a folder

I have a component that represents a "badge". There are hundreds of types of different badges. Every badge is an individual SVG file. The badge component receives a slug for the badge it would need to use. The slug matches the file name. For instance: best-cheesecake would hint we need to display best-cheesecake.svg in the badge control.
Up till now, I've been loading svg images using:
import TestSVG from "../../images/test.svg";
and displaying them using:
<img height="50px" width="50px" alt="my-test-svg" src={TestSVG}/>
It's critical to understand I'm using WebPack and all images get their filenames hashed.
My question is about the best practice if my control needs to load one of the hundreds of SVG files. can my import dynamically using the filename string as it's given to the control via the props where those files get a hashed filename by webpack? Is there a better way? Obviously there has to be a way other than importing all SVG files in the folder...
use require.context concept you can import all the files located in a directory.
Below is the reference to what I have used earlier -
function importAll(r) {
let images = {};
r.keys().map((item, index) => { images[item.replace('./', '')] = r(item); });
return images;
}
const images = importAll(require.context('path/to/the/folder', false, /\.(png|jpe?g|svg)$/));
Now you can retrieve a particular image by its file name as below
<img src={**images[svg]**} className = {imgClassName} alt = {svg} />
where svg is filename with extension, ex. file1.svg
My solution was this:
I moved my 500+ badge SVG files from /src/images, where I keep all images into /src/images/badges
I have no explicit reference to any of the badge SVG files in my code so WebPack ignores them when creating the dist folder (both dev and production).
I installed WebPack's CopyPlugin and I use it to force the copy of /src/images/badges/*.svg into a badges under my default dist folder (it doesn't care they're not explicitly used in my code)
In my code, I can now use this successfully <img src={"/badges/" + this.props.badgeName + ".svg"}/>

Optimize page load of multiple pages

I've a multiple page site. I created a single component enclosing all the texts/para/images. I've refered this component to all the multiple pages. I used a class name to hide/show the content to the respective pages via css. Problem is that the page load is slow. Since all contents are hidden using css they are still loaded making my site slow. Any help on this is much appreciated. Thanks
There are multiple things you can check.
Check the internet connectivity, I hope you have good internet connectivity to load the
page.
As you say there are multiple page with single component, check if you can move those into sub-component.
I am not sure how you're hiding those components use css property display:none if those are light weight.
you can use something like this:
<style type="text/css">
.hidden { display:none; }
</style>
render: function() {
return (
<div className={this.props.shouldHide ? 'hidden' : ''}>
This will be hidden if you set <tt>props.shouldHide</tt>
to something truthy.
</div>
);
}
// or in more modern JS and stateless react
const Example = props => <div className={props.shouldHide}/>Hello</div>
source

Can we override scss variables vai react props?Is it possible?

Its been 2 whole days i can't find any solutions.I have no solutions yets:( I'm having color states. I'll select from color picker a color, I'll update that particular color state:
Requirement is if I pick from color picker it must be passed from react js property or variables to scss variable n override them. it must be done via reacrjs to scss if it can be done from js to css then it can be done from reacr js to scss whats that one thing which m missing on it.
App.js
{
primary: '#1976D2',
secondary: '#424242',
accent: '#82B1FF',
error: '#FF5252',
info: '#2196F3',
success: '#4CAF50',
warning: '#FFC107'
}
ex: primary: '#1976D2' I'll pick in update to primary: '#ffffff' something like:
App.js
changeColor(e){
this.setState({primary:e.target.value}) // the value is updated to #ffffff
}
Now, I need to pass this.props.primary to .scss something like:
variables.scss
$primary:this.props.primary
login.scss
.btn{
background-color:$primary
}
my need is it must be dynamic if i pick from color picker it must be passed from react js property or variables to scss variable n override them
We can do it inline styling but I wanna do it the way defined above (via .scss).
Is it possible?or is the any better way?
something like this
https://vuetifyjs.com/en/style/theme
vuejs uses theme thats overides to scss variables
Vue.use(Vuetify, {
theme: {
primary: '#3f51b5',
secondary: '#b0bec5',
accent: '#8c9eff',
error: '#b71c1c'
}
})
can anyone please explain me how they r doing I'm not understanding
First. Just to clarify. You are not able to change SASS variables from browser. Because how it works: SASS code -> compiled into CSS -> CSS is sent from server to browser -> . So we actually should searching "how to override by JS something that was SASS variable in consistent way".
The only way I see doing that is using CSS custom properties. Take a look into spec to understand their caveats(e.g. it cannot be used as part of size value in #media) and about support in browsers.
I'm not really sure if SASS supports compiling variables into CSS custom properties. Take a look into css-vars mixin. With using
$css-vars-use-native: true;
you will get your variables exported as CSS custom properties. Unfortunately it means you need to change you existing styles to use var() from mixin for variables you want being able to override later
Then your code will be able to override any of custom properties as easy as
document.body.style.setProperty('--primaryColor', myColorValueFromDatePicker)
Take a look into fine and short article on how to change custom properties with JS
NB since custom properties uses the same cascade approach as any CSS you additional get ability to apply changed value on any part of DOM:
document.querySelector('#previewBlock').style.setProperty(....);
I would use Styled Components instead. There you can pass your styling by props
https://www.styled-components.com/docs/advanced

React : best way to inject Component in dynamically loaded HTML?

I'm new on React (I more at ease w/ jQuery or AngularJS). I have a special case and I don't find a good way to resolve it...
My app contains an area which is like a "document viewer". It loads an HTML content from the backend (via API, using Fetch) and inject it in the "viewer" component. The HTML content loaded looks like an "university report" (it's just a formatted text, only <span> and <p> with class="..." attributes, nothing more).
Ex : <p>Lorem ispum <span>some text</span> loreb bis <span>ipsum</span></p> ...
I load the content, and inject it this way in the render() of my component <Viewer> :
<div dangerouslySetInnerHTML={ getFreshlyLoadedHTML() } />
Easy, it works just fine !
But... Now, I want to inject some "interactive" components in the loaded HTML. For example, some button to give a feedback etc. The API must decide where to place the component between the words/nodes of the formatted text (HTML).
Ex :
<p> Lorem ispum <span>some text</span>
loreb bis <span>ipsum</span>
<MyFeedbackButton paragraph="1.3"/>
</p><p>Other Lorem Ipsum<p><span>...</span>
There, I'm stucked because I cannot use dangerouslySetInnerHTML if there are components inside the loaded HTML...
First attempt : I've tried modifying the API, and instead of sending the HTML in a string to the app, I send a custom JSON structure that represents almost the final JSX structure that I want. Then, in my react page, the render function only have to parse the JSON and build the JSX (here, a JsFiddle example if it's not clear : https://jsfiddle.net/damienfa/69z2wepo/34536/ )
It works, but I can't believe it's the good way...
I see a major problem : all the HTML node (span, p...) that I build from the render function are referenced by reactJs, is it really necessary ? Mostly, there are "dead" nodes (I mean, dom node that won't never changed, this is static formatted text).
Just take a look a all those "data-reactid" on nodes that never will be interactive...
What would be your advice on that case ?
What about my attempt with a JSON-structure sent by the API ?
Is there a way to say to react "do not reference that element" ?
Do you clearly see a better solution to my problem ?
Your current workflow is not very secure and subject to many potential errors and open doors, especially concerning code injection ...
The overload due to react tracking the nodes is not an issue, React could track 10 000 nodes and not have a problem (well actually on many of my apps React has more than 100 000 nodes to care about and it still rurns perfectly).
I see different solutions here:
If there are only 3 or 4 possibilities of dynamic components and order, you might have components like "templates" to which you would simple send text arguments. This is the safest and easiest option.
If it doesn't suit your use-case but the JSON file can contain only a limited set of components, the components should be located in your main app, and then rendered with custom props from the JSON. Actually given the structure of data you could consider using xml instead of json and build a xml tree that you would parse and render. Only components from your white list would be rendered and it would limit drastically the potentials security issues. If needs quite some work on the XML parser though.
If the JSON file can contain many many different and unpredictable components or if the behaviour of those components is largely dynamic and independant of your app, you might as well consider using an iframe, with its own JS and HTML, so that this part of the code is isolated from the rest.
Try using an inline anonymous function within the inner content from within React using JSX. It works! Just be careful about how you wire up the data so there isn't a route where a user can inject HTML from an input or text field.
<div className="html-navigation-button">{(() =>
{
const CreateMarkup = ( sNavItemName :string ) => {
return {__html: sNavItemName };
}
var sTextToAddHtmlTo = props.nextNavItem.name.toString();
sTextToAddHtmlTo = sTextToAddHtmlTo.replace( "/", "/<wbr>" );
return (
<div dangerouslySetInnerHTML={CreateMarkup( sTextToAddHtmlTo )} >
</div>
);
})()}
</div>
I didn't override the React internals of 'render()', but only used a React Component with props wiring to pass down data to it for rendering.
I added the hook for 'dangerouslySetInnerHTML' deep within the return content of the React Component so there would be no easy way to intercept and manipulate it.
As such, there is no 100% guarantee on safety, but that's where adding good security to web services, databases, and use of CORS and CORB would be helpful to lock down security risks.

Resources