I want to make a dynamic component. (the dynamic TAG will be a styled component -> emotion)
const Dynamic = ({ tag: Tag, children, ...rest }) =>
<Tag {...rest}>
{ children }
</Tag>
The component will be a styled component like:
const Column = styled(div)({ color: 'red' })
const Row = styled(span)({ color: 'yellow' })
This looks all nice, and working properly, BUUUUUT:
When I try use a DynamicComponent inside another DynamicComponent:
<DynamicComponent tag={Row}>
{
mapOver.map(item=>
<DynamicComponent tag={Column}/>
)
}
</DynamicComponent>
then for some reason the Dynamic children will use the Dynamic Parent's style.
Is there anything I missing?
P.S.:
If instead of using dynamic styles, I do something like this:
<Row>
<Column/>
</Row>
then the styles, classNames, styled tags, are applied properly.
To make it a little more clear:
As you can see the DynamicComponent's will use the parent's styles, classNames, styled tags... (NOT THE BEHAVIOUR I WOULD EXPECT)
Below example creating a dynamic tag name for a styled-component:
// All headings use the same styled-component "Heading" function
const StyledHeading = styled.div`
font-size: ${({level}) => 4/level }em; // <- dynamic font size
font-weight: 300;
margin: 0;
`
// the trick here is the "as={...}" to create dynamic tag name
const Heading = ({level = 1, children}) =>
<StyledHeading as={`h${level}`} level={level}>
{children}
</StyledHeading>
ReactDOM.render([
<Heading>Hello, world!</Heading>,
<Heading level={2}>Title 2</Heading>,
<Heading level={3}>Title 3</Heading>
] ,document.body
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/styled-components#4.4.1/dist/styled-components.min.js"></script>
Reference:
"as" prop documentation
There is a misunderstanding in the usage of styled-components as a tag is intended as HTML tag (input, div and so on). The best way is to define a StyledRow and a StyledColumn separately and use them with appropriate names. This will help also to make your code more readable.
Related
I have an a component that handles a list, and has some internal logic to determine if it should render as a ul, or as a div. If the list is empty, it renders as a div, and if the list has elements, it renders as a ul.
I'm making an API call to get the potential list of elements. When I loop through,
<ParentUlOrDiv>
{data.map(el => {
<div>test</div>
})}
<div>final row</div>
</ParentUlOrDiv>
The parent doesn't know the amount of elements it will have to render, so it plugs them all into one li, meaning that I'll get all sorts of visual misbehavior.
How can I return the complete, mapped list from data and pass that that to the ParentUlOrDiv as a child (as then it'd know the amount of elements it needs to render)?
If I just hardcode two divs instead of the map, I'm rendering correctly. But with a map, I get issues.
Use props.children.length to choose between an ul or the div.
Example were the children are wrapped in a ul and li if there are more then 1. Otherwise, it's just rendered in a div
const ParentUlOrDiv = (props) => {
console.log('ParentUlOrDiv got', props.children.length, 'children');
const WrapElement = props.children.length > 1 ? 'ul' : 'div';
const values = props.children.length > 1 ? props.children.map(c => <li>{c}</li>) : props.children;
return <WrapElement>{values}</WrapElement>;
}
const Example = () => {
const data = [
'Foo', 'Bar'
]
return (
<ParentUlOrDiv>
{data.map(d => <em>{d}</em>)}
</ParentUlOrDiv>
)
}
ReactDOM.render(<Example />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
The answer ended up being because of a mixture of dynamic / static content within the parent. It has to all be dynamic, coming from the map.
React Component
import React, {useState} from 'react';
import './CounterButton.css';
const CounterButton = (props)=>{
const [currentCount, setCurrentCount] = useState(0);
const handleClick = (event)=>{
if(currentCount == 9){
event.target.classList.toggle('bound-hit');
}
setCurrentCount(currentCount+props.incrementVal);
};
return (
<div class="count-container">
<button onClick={handleClick}>+{props.incrementVal}</button>
<p>{currentCount}</p>
</div>
);
};
export default CounterButton;
External stylesheet for this component
.count-container {
display: flex;
padding: 10px;
}
.count-container > button {
width: 50px;
margin: 0 10px;
}
.bound-hit {
color: red;
}
I have a react component and stylesheet for that component. In this case it toggle class bound-hit to the classList of button. I could select button using event.target and but I want to toggle this class to the <p></p> tag inside my div. My question is how can I select that p tag using event. p tag is like a sibling of button. div with class count-container is parent. I can also select parent div by event.target.parent but I want to select p tag and toggle class bound-hit to that.. How can I do that?
I don't think you need a React specific answer here.
In vanilla JS you can use the nextElementSibling method.
const handleClick = (event) => {
const p = event.target.nextElementSibling
}
Or instead you can do it in CSS with the adjacent sibling combinator.
.bound-hit + p {
// apply styles to the <p> that's just after .bound-hit in the DOM
}
However, if you "manually" add a class in a react component (meaning that this class gets added to the DOM without any representation in the state), some virtual DOM reconciliations might end up removing it.
In a lot of cases, this won't be a problem, but if it is, then you should use a state for it. Here's a simplified example of what that would look like:
const [pClass, setPClass] = useState('')
const handleClick = () => {
setPClass('bound-hit')
}
return (
<p className={pClass} />
)
The question shouldn't be "how to select a sibling" but "how to assign CSS class to the P element on [condition]".
If a React component directly has ownership over the (child) elements you can simple change the components state and apply it to the class list of the element using className.
Doing any DOM manipulation/traversing within a component is mainly bad form using React and overcomplicates the solution.
const CounterButton = (props)=>{
const [currentCount, setCurrentCount] = useState(0);
const [currentClass, setCurrentClass] = useState();
const handleClick = (event)=>{
if(currentCount == 9){
setCurrentClass('bound-hit');
}
setCurrentCount(currentCount+props.incrementVal);
};
return (
<div class="count-container">
<button onClick={handleClick}>+{props.incrementVal}</button>
<p className={currentClass}>{currentCount}</p>
</div>
);
};
In normal JavaScript you can grab an element by its id and add a style to it.
For example:
var element = document.getElementById("myElement");
element.style.backgroundColor = "#f5f5f5";
My question is how you can do this in react. Is it even possible to add this style?
In react im using onChange in a function outside the render(). I looked at the React DOM for styling and tried but since styling is in different function it will tell me how the variable is undefined.
this is my code:
ChangeImage() {
var imgStyles = {
backgroundColor: '#000',
padding: 5,
}
}
render() {
return (
<div className="class">
<div className="img-surround">
<img
src={this.state.file}
id="img"
style={imgStyles}/>
</div>
Everything is working except styles and I even tried putting in different functions
If you want to render the element with the style you can return the element like this in a react functional component:
return <div style={{backgroundColor: "#f5f5f5"}}></div>
If you want the element to only have that style in a certain condition you can use the useState hook in a react functional component:
const [myState, setMyState] = useState(false);
return <div style={myState && {backgroundColor: "f5f5f5"}}></div>
And you should change myState's value using setMyState however you like. For example:
const [myState, setMyState] = useState(false);
return <div onClick={() => myState ? setMyState(true) : setMyState(false)} style={myState && {backgroundColor: "f5f5f5"}}></div>
In this example whenever you click on the div the style is added or removed by case
I am calling a component "MyRadioButton" with following props:
<MyRadioButton
label="Radio Group"
theme="custom-red" //this line
error="Field is required "
radioBtns={options}
id="radioBtns"
name="radioBtns"
getValue={this.getValue}
/>
I have created a react component "MyRadioButton" that will accept color name(theme) as props.
export const MyRadioButton = props => {
const {theme} = props;
return (
<div className="my-radio-buttons"> // need to use theme here
<input
onChange={onChange}
type="radio"
/>
</div>
)}
Based on this prop i want to assign the variable in my components scss file, which will take the color code from my custom defined color pallet.
my-radio-button.scss
/* custom color pallet */
$custom-orange: #F060D6;
$custom-red: #BB532E;
$custom-blue: #4C9FEB;
.my-radio-buttons {
.input{
border: 2px solid $custom-red; // i want to assign the color variable based on input prop value to this property
}
}
I have already tried setting variable at css root with javascript and accessing it with variable function var(), it works fine.
But because of some limitations i dont want to use that approach.
also because the color pallet list is huge, i dont want to use separate classes for all of them.
I am looking for some other solution or different approach.
So you can use a combination of custom css variables and your passed theme property. In you css, you would define the basecolor of the border for example:
.my-radio-buttons {
--theme-color: red;
input {
border: 2px solid var(--theme-color);
}
}
This can be updated by your components via componentDidMount or useEffect with the passed theme:
const MyRadioButton = props => {
const { theme } = props;
React.useEffect(() => {
const input = document.querySelector(".my-radio-buttons input");
input.style.setProperty("--theme-color", props.theme);
}, []);
return (
<div className="my-radio-buttons">
<input />
</div>
);
};
Depending on your code style, you can replace the querySelector with a ref.
I need to update the CSS, and naturally I used jQuery, but I'm told not to use jQuery with React.
How would I do this properly. I can add more code if needed. I'm simply toggling the bottom border of a div
toggleMarker () {
if (this.state.previous && (this.state.current !== this.state.previous)) {
$('#nav_' + this.state.previous).css("border-bottom", '');
}
if (this.state.previous !== this.state.current) {
$('#nav_' + this.state.current).css("border-bottom", this.color);
}
this.setState({previous: this.state.current});
}
You can manipulate components style inline and you can give conditions according to state variables.
Example
render(){
return(
<div style={{ borderBottom: ((this.state.previous && (this.state.current !== this.state.previous)) ? 'none' : 1) }}>
// ...
</div>
)
}
When it comes to react, there are many ways to style a component including inline styles, define styles in css and import, using styled components and also using some small JS libraries e.g. classnames.
classnames supports any JS expression as class name to your HTML element. You can explore more using above link.
Just a simple example:
import styles from './yourcss.css'
import { classnames } from './classnames/bind'
const cx = classnames.bind(styles)
<div className={cx('divStyle')}>
</div>
I would suggest to have inline CSS with reference from variable in the state. consider this,
//define state
this.state={
toggleState : false}
//have toggler function
togglerFunction(){
var temp = this.state.toggleState
this.setState({
toggleState : !temp})
}
//in render you can have your element like this
render(){
...
//start of your element suppose a div
{this.state.toggleState == false ? <div style=
{{borderBottom:"YourValueForFalseHere"}}></div>:<div style=
{{borderBottom:"YourValueForTrueHere"}}></div>}
//...End of your element
...
}