Setting className string dynamically Javascript \ React - javascript

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]}`

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

vue3-style-components render duplicate classes in dom

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.

Positioning a component in .jsx-file and not via a .css-file

I'm looking for a solution how to position a component in a .jsx-file instead of a .css-file because I have multiple files of the same component, but each one is responsible for different tasks so I need them in different positions of the page.
I don't want to have multiple copies of the same file with only a minor change of the css class - I would rather like to add changes to the .jsx-file, or if you know how to achieve that using a .css-file, please let me know.
Example:
I have a 'Fish'-file which gives the basic structure of what the fish will look like.
I have to make multiple fish files(i.e fish.jsx, fish1.jsx, fish2.jsx) because they each use a different css class for their positioning. How can I reduce the amount of Fish.jsx to one either by adding to the original fish.jsx or .css-file used?
The way I would approach your problem would be something like this
import Fish from './Fish';
import './Fish.css';
// other stuff
const FishContainer => (
{[...Array(10)].map( (_, i) => <Fish className=`fish-${i}` /> )}
);
export default FishContainer;
And in your css file
.fish {
&-1 {}
&-2 {}
// etc
}

Augmenting Object in React

I am working on a project where I have to attach a few utility functions to Javascript Object object as follows:
function isEmpty(a, b) {...}
Object.prototype.isEmpty = isEmpty;
Now the problem i am facing is, since I am working with react, I am guessing the above code is also attaching the isEmpty function to the constructed Components. And this works fine as long as I don't use the native html tags i.e. div, span inside my components which is not possible. I get the following warning
Warning: Unknown prop `isEmpty` on <div> tag.
Remove these props from the element. For details, see
https://facebook.github.io/react/warnings/unknown-prop.html
when I use the native html tags. Is there any way to augment the Object object without getting this error in react?
The problem is that an object extension like this is enumerable. You need to use defineProperty
BTW: this is still a bad idea
When you write jsx tags in react it gets transpiled to objects (React elements).
So
<div id="test">
</div>
is transformed into following object -
var divElement = React.createElement("div", { id: "test" });
Now since you are attaching
function sum(a, b) {...}
Object.prototype.sum = sum;
It gets attached to every objects present.
May be you should consider providing a Util.js which will contain all utility methods and do not attach to Object prototype. Because It can cause undesired side effects.
You can import Util.js where ever you need and use those methods.
e.g. -
module.exports = {
sum(a, b) {...}
};
Problem
All JSX elements are first created as objects (WitVault explains how JSX is transpiled to plain JS that can run in the browser). React takes those objects and their properties that React supports and maps them to DOM elements. If there are properties that React does not know about, it will show a warning because it's likely a case of either "you don't know what you are doing" or "you made a typo", and therefore you won't get the behavior that you expect.
Since you edited the prototype of Object, all objects, also those created by React, get the property sum, and for primitive html elements React does not know how to map a sum property.
As Juan Mendes pointed out, extending native objects is bad practice. If you extend Object.prototype in a React project you really cant avoid the problem you are experiencing.
Solution 1: Export/Import util function
Since React comes with browserify you can instead import utility methods. This has two advantages:
You don't need to extend native objects
You clearly express where the methods used come from, be cause they have an import statement in the file where it's used.
In ES6
// util/objectSum.js
export default function objectSum(object) { ... };
// anotherFile.js
import objectSum from 'utils/objectSum.js'; // assuming utils/ is directly under the root path of your project (root path can be configured in webpack.config if you use webpack)
const object = ?; // the object you want to sum
const sum = objectSum(object);
In ES5
// util/objectSum.js
module.exports = function(object) { ... };
// anotherFile.js
var objectSum = require('utils/objectSum.js'); // assuming utils/ is directly under the root path of your project (root path can be configured in webpack.config if you use webpack)
const object = ?; // the object you want to sum
const sum = objectSum(object);
Solution 2: Using ES6 classes
In ES6 you can also create a class with the sum method. Here's an example:
class NumberList {
constructor(arrayOfNumbers) {
this.numbers = arrayOfNumbers;
this.sum = this.sum.bind(this);
}
sum() {
return this.numbers.reduce((sum, next) => sum + next, 0);
}
}
const numberList = new NumberList([1, 2, 3]);
numberList.sum(); // -> 6

How to render ReactJS component from outside of JS class it's defined in and/or with dynamic id element name?

If I defined a ReactJS class in say dialog.js:
var divStyle = {
padding: '15px',
paddingBottom: '12px'
};
var Dialog = React.createClass({
render: function() {
return (
<div style={divStyle}>...</div>
);
}
});
In the above I define a class.. but in every example I see there is also the React.renderComponent(<Dialog/>,document.getElementById('someId'));
The problem I have with this is.. if I want to use this component on different pages with different IDs to render it at, or perhaps for several different IDs, I can't hard-code the id in the actual class it's at. I supposed I can pass in the ID in some manner??
But furthermore, I also want to be able to render the component in say a different JS class, one loaded AFTER the loading of this js class. I have an app.js class for my SPA app, that is loaded last. In it, when a user clicks a link, I may want to only render this component at that time. I do realize in the render method I can control in some way whether or not it's actually rendered.. but my thinking is also to not even bothering inserting it into the DOM unless an action occurs. Sort of like lazy insertion I guess?
I've tried in my app.js:
function () {
React.renderComponent(<Dialog/>,...);
}
but obviously this doesn't work as this JS is not a JSX JS file. So I tried using React.renderComponent(Dialog,...); thinking the Dialog class is globally defined, so it would be available, but that too fails.
So how do I use the Dialog class in another function to render it?
I think you're getting something conceptually wrong here:
but in every example I see there is also the React.renderComponent(<Dialog/>,document.getElementById('someId'));
The fact that the short examples are followed by a renderComponent call is for the trivial reason to show the demos. Once again, keep in mind that in React, <Dialog /> desugars to Dialog(). If you don't call it nothing happens.
So, simply don't call until you need it.
Also, I don't understand what you mean by:
but obviously this doesn't work as this JS is not a JSX JS file
Since you can just process that file through JSX?
If you're having trouble mentally mapping JSX to functions and vice-versa: try the live compiler here: http://facebook.github.io/react/jsx-compiler.html

Categories