vue3-style-components render duplicate classes in dom - javascript

In my vue3 project, I used vue3-style-components, but when I add class in components which is created by vue3-style-components it renders duplicate classes.
like this:
How can I prevent duplicate class rendering?
This is my vuejs code:
And This is Style-components Code:

I know this doesnt answer your question - but I would recommend destructing your props/theme not on the individual css attributes but as a wrapping function and then using the css function from styled-components as well, like:
import Styled, { css } from 'vue3-styled-components'
const props = ['type', 'shape']
const ButtonStyled = Styled('button', props)`
${({ type, theme }) => css`
background: ${ type !== 'default' && theme[`${type}-color] + '' }
`}
`
Because this operation can be expensive, so performing the callback to get the type and theme variables over and over could really slow down performance.
Also I would consider whether or not you need to check the type here or if that maybe is a task better suited for the theme - if there is no theme["default-color"] then you can just use the nullish coalescent operator as such theme[${type}-color] ?? '' to the same effect, if will then default to an empty string if the first variable is null or undefined.
As for your actual issue - its difficult to tell unless you supply some information about where these classes are actually applied, because I cant see any of them being set in the code you supplied.

Related

Using conditional operator inside Element.classList.add()

I'm making an application that uses TypeScript, and as a templating language I'm using Svelte.
That allows me to create DOM elements with classes that can change in realtime according to a variable, thanks to ternary operator. For instance:
<div class="{theme == "dark" ? "bg-black" : "bg-white"}"> Hello </div>
The thing is, my application has to dynamically generate some DOM elements. That makes me create some divs using the following piece of script:
const parentDiv = document.getElementById("parentDiv");
const childDiv = document.createElement("div")
childDiv.classList.add(theme == "dark" ? "bg-black" : "bg-white")
parentDiv.appendChild(childDiv)
In this case, the conditional operator is just calculated when .add() is called, which happens once. There is no "realtime calculation" of the value like in the first method above. How do I handle this ?
If you are not creating the elements via plain Svelte markup you are probably doing something wrong. There are various directives that help, e.g. {#if} and {#each}.
In some cases it makes sense to imperatively add things, but even then, you should add components not plain DOM elements. You can instantiate any component using the client API, e.g.
new MyComponent({ target: document.body })
Anything inside the component then can use the Svelte mechanism that automatically update. If you need to have reactivity from the outside, you can pass a store as a property or import a global store from within/load it from a context.
(Would recommend making the theme a store. Either global or passed through props/contexts.)
Note on contexts with the client API: They have to be passed explicitly; to inherit existing contexts you can use getAllContexts:
// somewhere at top-level
const context = getAllContexts();
// ...
new MyComponent({ ..., context })

Cannot Get Values from Prop in Twin.macro

You can see an example of what I am trying to do here: https://codesandbox.io/s/vibrant-leaf-qj8vz
Note: this particular example is using Twin.macro with Styled Components. On my local computer I tried the same thing with the same results using Twin.macro with emotion/next.js.
Here is a sample component illustrating what I am trying to do:
import React from 'react'
import tw from 'twin.macro'
const Acme = ({ children, type }) => <div css={[tw`${type}`]}>{children}</div>
export default Acme
Here is how I would use that component: <Acme type="text-2xl">Text Goes Here</Acme>
My expectation is that I will be able to style this instance of the <Acme /> component via the tailwind css classes that I pass into the type prop. Instead, I get the following error message:
/src/components/Acme.js: twin.macro: Property value expected type of string but got null Learn more: https://www.npmjs.com/package/twin.macro
When trying to figure this out, I noticed something interesting that may be relevant. Here is a variation of the code that does work:
const Acme = ({ children, type }) => {
const typeClass = 'text-2xl'
const typeObj = {
class: 'text-2xl',
}
return <div css={[tw`${typeClass}`]}>{children}</div>
}
export default Acme
Note that I have created a variable typeClass and set it to the same tailwind css class. Note, in particular, the following line of code:
css={[tw`${typeClass}`]}
I have replace the prop type with the variable typeClass. This works. But now, instead of using the variable typeClass let's use the object typeObj that I have created as follows:
const Acme = ({ children, type }) => {
const typeClass = 'text-2xl'
const typeObj = {
class: 'text-2xl',
}
return <div css={[tw`${typeObj.class}`]}>{children}</div>
}
export default Acme
This does not work and produces the same error:
/src/components/Acme.js: twin.macro: Property value expected type of string but got null Learn more: https://www.npmjs.com/package/twin.macro
This is so even though typeClass === typeObj.class evaluates to true.
I don't know if this is helpful, but perhaps it can help indicate a solution. If I can get the type prop to behave like the typeClass variable then hopefully this would work.
Either way, any idea why this is not working and how to fix it?
Thanks.
I found the answer (meaning that someone else answered it on a different site). Here is is. I have to rewrite both the Component and the usage of the component as follows:
// Acme.js
const Acme = ({ children, type }) => <div css={[type]}>{children}</div>
---
// App.js
import tw from "twin.macro"
<Acme type={tw`text-2xl`}>Text Goes Here</Acme>
I have tried this out and it works.

Is it possible to lazy render component in StencilJS?

As you want to build complex component, it would be great if you can wrap any DOM with component such as "lazy-load" component with condition (#Prop() condition: boolean) so to illustrate what I want:
<lazy-load condition={some boolean condition, like certain link get clicked and section is now active}>
<data-fetch>
</data-fetch>
</lazy-load>
in this example, "data-fetch" will make a HTTP call to grab some large data, and I want to defer this component added to DOM until condition we specify in the lazy-load component to be true.
So I started to implement render() of lazy-load component as something along the line of
#Prop() condition: boolean;
render() {
if(!this.condition) {
return null;
}
return (
<slot/>
);
}
and try to use it as
<lazy-load condition={false}>
<data-fetch>
</data-fetch>
</lazy-load>
but no matter what I tried, data-fetch component get added to DOM (and while we can set visibility to hide element, we would waste HTTP call) I understand I can put the same condition in the data-fetch itself and then not make a fetch call when condition is false, but if possible I want generic wrapper component to achieve this (if you are familiar with AngularJS or Angular, I want to find a way to do equivalent of ng-if and *ngIf off of generic wrapper component)
Maybe this is a limitation due to how "slot" tag supposed to work? (also, I'm using it with #Component({shadow: false}) so I know I'm not using standard shadowDOM from the web component spec so maybe what I'm trying to do is not feasible?
Thank you very much for your time in advance to even read this question and I appreciate any help I can get. I feel if we can do this, we might be able to build component that can quickly differ loading until whenever we feel it should load/render.
Yeah it's an issue with not using shadow: true, because in the polyfill the slotted content just becomes part of the light DOM (but gets placed where the slot element is). Beware that even if you enable Shadow DOM, it'll still fallback to the polyfill if the browser doesn't support it. You could raise an issue about this in Github but I'm not sure if/how it would be possible to solve this "dynamic slot" problem.
But I think you can take a simpler approach:
{myCondition && <data-fetch />}
That way the data-fetch element will only be added once the condition becomes true.
You could also refactor this into a functional component:
import { FunctionalComponent } from '#stencil/core';
interface Props {
if: boolean;
}
export const LazyLoad: FunctionalComponent<Props> = ({ if }, children) =>
if && children;
import { LazyLoad } from './LazyLoad';
<LazyLoad if={condition}>
<data-fetch />
</LazyLoad>

Setting className string dynamically Javascript \ React

In my current project, I'm setting up mobile support the following way:
1) I'm creating all the relevant components both for desktop and mobile. On most of my pages, they're the same.
2) I'm creating two .scss files, one for desktop and one for mobile (determined by a media query covering the entire file)
3) I'm then attaching both styles in the className of my components, and then only the relevant file gets set. It looks like this:
// Styles
import styles from '../../components/Timeline/timeline.scss';
import mobileStyles from '../../components/Timeline/mobileTimeline.scss';
// Example component
<Row className={`${styles.container} ${mobileStyles.container}`}>
<div className={`${styles.myComponent} ${mobileStyles.myComponent}`}/>
</Row>
It works great, but in order to make the code a bit cleaner, I decided to write a helper function to generate the entire string for the className ->
const setStyle = styleName => `${styles.styleName} ${mobileStyles.styleName}`
However, setStyle always returns 'unidentified' (*the function is defined after the styles imports of-course)
I think I understand why it happens, but I wonder, is there a way we could dynamically access style object properties like that?
Thanks in advance!
To get a property from an object given the key name in a variable, use the bracket notation:
const setStyle = styleName => `${styles[styleName]} ${mobileStyles[styleName]}`
This assumes that styles and mobileStyles are available in the scope of the function, otherwise you would also need to pass them:
const setStyle = (styleName, styles, mobileStyles) => `${styles[styleName]} ${mobileStyles[styleName]}`

Can I make styles apply to classes other than the first defined in a file?

I have inherited some code and I've been manipulating it, but I came across something that makes me scratch my head.
I am not sure whether the issue I am having relates to specifically to react.js or more generally to CSS / javascript...
In this code, we make use of react.js withStyles.
I have set up a sandbox to demonstrate.
First thing in the file, we define some styles that can then be applied to page elements, e.g.
const styles = theme => ({
buttonGroup: {
width: "250px",
display: "flex",
flexWrap: "wrap",
}, ...
then, when we define a class, we can get access to these styles by doing a const { classes } = this.props , e.g.
class MyButtons extends React.Component {
render() {
const { classes } = this.props;
return (
<div className={classes.buttonGroup}>
{three_buttons.map(e => (
<Button className={classes.a_button}>{e}</Button>
))}
</div>
);
}
}
That is all fine and works.
What I've tried is to, in the same file, define a second class, which I then call from the first (to make a component within a component).
However, the const { classes } = this.props does not seem to gain me access to the styles defined at the top, and when I try to set className={classes.buttonGroup} in the second class, I get an error
TypeError: read property 'buttonGroup' of undefined
I am pretty sure I can overcome this by simply defining the second class in a separate file like I usually do, but I would like to understand why this does not work.
You are not passing the styles as props to MyOtherButtons Component and hence you are getting this issue. Pass the classes as props and things would work as expected. It works for MyButtons component since you are passing the styles using withStyles syntax.
Check the working link https://codesandbox.io/s/m3rl6o2qyj

Categories