Is there a simple way in which I can pass a "large" object of element attributes via an object?
For example:
const myAttrs = {
one: "one",
two: "two",
...,
fifty: "fifty"
};
return(
<MyElement data={myAttrs}>Some label</MyElement>
);
Such that I can access and set all of these values in the defined element itself...
const MyElement = styled.a.attrs(props => ({
// I want to break down and set each object prop here, rather than
// do it all individually like...
//
// one: props.one,
// two: props.two,
// ...
}))`
// CSS here...
`;
Use spread operator
<MyElement {...myAttrs}>Some label</MyElement>
const MyElement = styled.a.attrs({one, two, three} => ({
// you can access these properties directly by using one for props.one and so on
Pass large number of props from parents to child
<Content
accounts={this.props.accounts}
showBalance={this.props.showBalance}
withdraw={this.props.withdraw}
deposit={this.props.deposit}
current={this.props.current}
depositAmount={this.props.depositAmount}
handleDeposit={this.props.handleDeposit}
handleRm={this.props.handleRm}
amounts={this.props.amounts}
getWithdraw={this.props.getWithdraw}
/>;
could be replaced with
<Content {...this.props} />
You can destructed in child component
const {accounts,showBalance,withdraw,.....,getWithdraw} = this.props;
On the back of the spread work done by #kooskoos, I found a way in which I think I can include all of the attributes I like.
Basically we include the spreaded object in the HTML
<MyElement {...myAttrs}>Label</MyElement>
then in the const we can access these props easily by (instead of individually highlighting the attributes), we can simply write as such:
export const MyElement = styled.a.attrs(props => props)`...`
and this will pass through ALL props defined in the object, and attach them to the HTML element as you have defined them.
<MyElement one="one" two="two" three="three" ...>Label</MyElement>
Related
Endless Variables
Often I find myself assigning numerous DOM elements to a list variables, and naming them gets increasingly difficult as things grow more complex.
const header = document.querySelector('header')
const headerWrapper = header.querySelector('.header-wrapper')
const headerH1 = headerWrapper.querySelector('h1')
const headerH2 = headerWrapper.querySelector('h2')
// etc...
Object Notation
It occurred to me try referencing the DOM hierarchy using Objects instead and came up with something like this:
// assign DOM elements to object properties
const homePage = {
headerContainer: document.querySelector('header'),
headerWrapper: document.querySelector('header>.header-wrapper'),
header: {
h1: document.querySelector('header>.header-wrapper>h1'),
h2: document.querySelector('header>.header-wrapper>h2'),
}
}
// update element content
homePage.header.h1.innerText = "New Header Text"
This method is flawed however:
Parent nodes must be given unique keys to reference directly
Objects can't refer to themselves until they have been initialized, so I can't have h1: this.headerContainer.querySelector('h1')
Instead of relying on headerContainer it would be better to have homePage.header, as well as the h1 tag within that container with homePage.header.h1 but I'm not sure if that's possible with JavaScript/JSON.... to my mind it would be something like this:
// pseudocode
const homePage = {
header:
document.querySelector('header'),
{
h1: this.header.querySelector('h1'),
h2: this.header.querySelector('h2'),
}
}
// update element content
homePage.header.h1.innerText = "Cleaner Syntax"
Is there some other means of doing this? Using Classes or Object Constructors perhaps?
Edit 1:
Solution: Using Classes
I think what I want to work out is how to store elements as an attribute of another element or object property, and I think I have worked out how to do that using Classes:
class Page {
constructor(body = document.querySelector('body')) {
this.header = body.querySelector('header')
this.header.h1 = this.header.querySelector('h1')
}
}
const homePage = new Page()
homePage.header.style.backgroundColor='yellow'
homePage.header.h1.textContent = "Hello World"
New to React.
I am trying to create a set of 12 range sliders, in two columns of six. Each column is declared separately and each column independently declares the instance of the slider. Each range slider uses the package react-slider and has a currentValue state that is passed down as a prop along with the setCurrentValue method as the setState.
My issue is that when I display the values below each slider, they all sync up together, ie. they appear to all use the same variable. How do I get React to differentiate between each 'instance' as such? SliderValueText just returns an element with the value displayed.
The production value is a boolean that just tweaks the title slightly.
The Slider element:
// imports here
export const Slider = (props) => {
const { currentValue, setCurrentValue, title, production } = props
return (
<>
<ReactSlider
className='customSlider'
key={title+production}
thumbClassName='customSlider-thumb'
trackClassName='customSlider-track'
markClassName='customSlider-mark'
min={0}
max={100}
defaultValue={0}
value={currentValue}
onChange={(value) => setCurrentValue(value)}
/>
<br/>
<SliderValueText currentValue={currentValue} />
</>
)
}
The SliderSet element:
// imports
export const SliderSet = (props) => {
const { currentValue, setCurrentValue, production } = props
return (
<>
<Slider
currentValue={currentValue}
setCurrentValue={setCurrentValue}
title='Lorem Ipsum'
production={production}
/>
// 5 further slider declarations here, all identical but with different titles
</>
)
}
I have tried using the key prop and a map (below) and I have tried using an array as the currentValue state declaration in the App.js file but I cannot figure out how to use setCurrentValue with an array (below but further).
In this instance, titles is an array of all of the titles for each individual slider.
const num = 6
const nums = new Array(num)
const element = [...nums.keys()].map(i => <Slider
key={i+titles[i]+production}
usableKey={i}
title={titles[i]}
production={production}
setCurrentValue={setCurrentValue}
currentValue={currentValue}
/>)
return (
<div>{element}</div>
State Array
// App.js
const [currentValue, setCurrentValue] = useState([0, 0, 0, 0, 0, 0])
// No idea how the usage for this works
Any and all help is appreciated :)
You need to pass different values to each slider, and different functions to update that state.
Many ways to go about it. Here is one example.
export const App = () => {
const [sliderValue, setSliderValue] = useState(Array(12).fill(0));
return sliderValue.map((val, index) => (
<Slider
currentValue={val}
setCurrentValue={(newVal) => {
const newSliderValues = [...sliderValue];
newSliderValues[index] = newVal;
setSliderValue(newSliderValues);
}}
/>
));
};
When the state is an object (like this array) the internals of react determines if the object has updated by doing what's called a shallow comparison. It just checks if it's the same object or not, it doesn't examine the actual content of the object.
For that reason, when updating the array of slider values we first make a shallow copy of the array. Then update that shallow copy and finally set the state of the new array.
I have a span which has a className . The span has two state, one is like
<span key={uuid()} ref = {colorRef} className = 'singleWord'>{word}</span>
and the other is like
<span key={uuid()} ref = {colorRef} className = 'singleWord redword'>{word}</span>
Now what I want is to check the className value like className.classList.contain("oneClass') which we do in vanilla js but I need in react hook(may be with ref hook) and also add className and remove className . also is it possible to check the length of className .for example 'singleword redword' should give length 2.
So overall I need -
add or remove classes
check length
check if class contains or not
I need to check them in react hook
could anybody suggest how to do ??
Thanks in advance
You can store in state an array of classes and use that to get all the information you need plus it gives an easy way to toggle any class in a set of classes.
basically this handleClasses function takes a class argument and then checks whether that class is already in your state. If it is, it removes it, if it's not, it add it to the state. Then in your span you join all the classes in the array in state to make a single string of classes.
Also you get the number of classes applied to your element very easily by doing classes.length
import React, { useState } from 'react';
const Component = () => {
const [classes, setClasses] = useState(['singleWord']);
const numberOfClasses = classes.length;
const handleClasses = (c) => {
setClasses(classes.includes(c)
? classes.filter((cls) => cls !== c)
: [...classes, c]
)
}
return (
<span
onClick={() => handleClasses(passClassToToggleHere)}
className={classes.join(' ')}
>
text
</span>
)
};
If you are using colorRef = useRef() hook, then you can obtain the target node by calling colorRef.current and proceed with vanilla js methods like
colorRef.current.classList.contain("oneClass') and so on.
I want to display my custom Tire element or simple div (depending on media queries) with different values on each call. But the ternary operator doesn't work (always displays Tire element, never div) and I'm getting "undefined" instead of string "1" that should be it's content.
Would anyone be so kind as to explain where I went wrong?
const Content = () => {
const isDesktop = window.matchMedia('(min-width: 110em)')
const displayTire = (props) => (isDesktop.matches
? <Tire className={`${props.title}`}>{`${props.content}`}</Tire>
: <div className={`${props.title}`}>{`${props.content}`}</div>)
displayTire.propTypes = {
title: PropTypes.string.isRequired,
content: PropTypes.string.isRequired
}
return (
{displayTire('pneu1', '1')}
)
export default Content
The first argument you pass is 'pneu1'.
This is assigned to the props variable.
You then read props.title.
Strings don't have title properties so the value is undefined.
Your prop types says that the first argument should be an object which contains two properties (title and content).
Pass an object that matches the definition instead of two plain strings.
You're not actually providing a title and content to the props of displayTire.
To do that, you should have done:
return (
{displayTire({ title: 'pneu1', content: '1'})}
)
I am new to React.
I used ReactIntl.FormattedPlural to format the plural.
<FormattedPlural
value={10}
one='message'
other='messages'
/>
It works when I place this component in the render() function. However, I have a scenario that I want to get the String and passed into a function.
Instead of Using {value ==1 ? 'message' : 'messages'}, can I use ReactIntl.FormattedPlural to achieve this?
In any case, in the end you need to put it into the render.
Because the FormattedPlural is a component. To display it, you need to render it. It's how React works.
You can save it as variable:
const text = (<FormattedPlural value={10} one='message' other='messages' />)
For example if you have a condition and need to decide which string to render.
But in the end, the text should passed to the render to be displayed for user.
Derived from FormattedPlural implementation:
const { formatPlural } = useIntl()
// get the plural category ("one" or "other")
const pluralCategory = formatPlural(count);
const options = {
one: "message",
other: "messages",
}
const plural = options[pluralCategory as 'one'] || options.other;
See also: https://formatjs.io/docs/react-intl/api/#formatplural