Here I created a sample example. As you can see I add applyColor Style object to apply styles.
import React from 'react'
const applyColor = {
color: 'red'
}
export const App = () => {
const renderData = () => {
return (
<>
<section style={applyColor}>
<p>Paragraph</p>
<div>
<h2>Heading</h2>
<span>Span</span>
</div>
</section>
</>
)
}
return <>
{renderData()}
</>
}
How can I modify applyColor Object to access the child from parent element <section />?
And I dont want to use any third party like styled-components and material-ui/styles.
Thanks:)
In short, no way unless you do it directly to the child.
<div style={{ color: red }} />
That's what the style is made for. However, everything is coded as Javascript code, so you can do it manually via props.
return <Child color="red" />
const Child = ({ color }) => {
return <div style={{ color }} />
}
Every big UI system has their unique way of handling this, ex. Material and etc.
But trusted me, unless you are making small css changes, styled-components like Css-in-Js approach isn't avoidable. Because passing the flag from Parent to Child is just too much work to manage by your own, not difficult but tedious. This is just my two cents.
Or you can use a traditional CSS approach, just put everything through CSS class. This way you can just include one copy of css. But the problem of that approach is that you can't pass a flag into the CSS so in the end you come back to the same boat above :)
Related
I came across the Component Composition Design Pattern in React, which is said by the tutor to be analogue to inheritance in OOP.
My question is, if I want to extend the defined props of the Button, how do you do this?!
SIDE NOTE: I know you would do this with CSS in the first place, but I
ran outta ideas to customize the tutor's example.
In the second code snippet I tried adding both, borderColor: "blue" and color="red" to the style attribute of the BigSuccessButton to try different approaches of appending stuff.
But with the style attribute the entire content defined in the Button Component will be killed. So I will only see blue borders.
So I thought of adding a new prop and using it.
But if last mentioned is the way to do this, how can I append this thing?
Those are the Composition Components, with Button being the Super Class:
export const Button = ({ size, bgColor, text, ...props }) => {
console.log(props);
return (
<button
style={{
padding: size === "large" ? "32px" : "8px",
fontSize: size === "large" ? "32px" : "16px",
backgroundColor: bgColor,
}}
{...props}
>
{text}
</button>
);
};
export const DangerButton = (props) => {
return <Button {...props} bgColor="red" />;
};
export const BigSuccessButton = (props) => {
return (
<Button
{...props}
size="large"
bgColor="green"
/>
);
};
Here I wanna add text color to BigSuccessButton:
import { BigSuccessButton, DangerButton } from "./Composition";
function App_FuncProg() {
return (
<>
{/* <RecursiveComponent data={nestedObject} /> */}
<DangerButton text="Danger" />
<BigSuccessButton text="Yippieh!" style={{borderColor: "blue"}} color="red" />
</>
);
}
export default App_FuncProg;
You've kind of mixed two patterns -- passing props and mapping them into styles; and trying to override the style prop. Passing props probably isnt the way because you dont want to end up having to map new props to the style object every single time you want to customize a new property (though design system libraries like Chakra do do this internally, but its comprehensive and you don't want to reinvent the whole wheel here).
That said, your mapping of size is more acceptable because it actually has semantic meaning, it actually does something (picking the pixel size of fontSize and padding). So I kept that part.
What you really want here I think is to add support for merging the style prop.
This sort of "pass through" approach gives you a high degree of flexibility at low cost by exposing the entire style CSS api to the parents; whilst at the same time, retaining the default styling. The downside is its maybe a little more ugly. Sometimes, if you want more control, you'd go purely with the mapping approach and add specific support via dev-defined props for the things you want exposed. Like you did with the size prop. It really depends on what you want your component API to look like.
A smaller thing: you really want to spread the props after the default ones if you want them to be overridable. The order matters.
export const Button = ({ text, size, style, ...props }) => {
console.log(props);
return (
<button
style={{
padding: size === "large" ? "32px" : "8px",
fontSize: size === "large" ? "32px" : "16px",
...style,
}}
{...props}
>
{text}
</button>
);
};
export const DangerButton = ({style, ...props}) => {
return <Button style={{backgroundColor: 'red', ...style}} {...props}/>;
};
export const BigSuccessButton = ({style, ...props}) => {
return (
<Button
size="large"
style={{backgroundColor: 'green', ...style}}
{...props}
/>
);
};
import { BigSuccessButton, DangerButton } from "./Composition";
function App_FuncProg() {
return (
<>
{/* <RecursiveComponent data={nestedObject} /> */}
<DangerButton text="Danger" />
<BigSuccessButton text="Yippieh!" style={{borderColor: "blue", color: 'red'}} />
</>
);
}
export default App_FuncProg;
An example of the opposing "first class props styling api" approach is Chakras api: https://chakra-ui.com/docs/styled-system/style-props. But again, this is obviously a very complete third party library and theyve spent a lot of time making this nice to use and expose every single option.
SIDE NOTE: I know you would do this with CSS in the first place, but I ran outta ideas to customize the tutor's example.
To be honest, in react, you don't usually go with separate CSS files like this. "CSS-in-JS" is now the norm. So inline styling like this is actually quite normal and not frowned on. There's whole libraries built around the philosophy (not necessarily using style attribute, but embedding the CSS in the component -- these things are complex and inject CSS into the DOM and then dynamically create class attributes). Have a look at styled-components and emotion. And Chakra etc has their own baked in based on things like emotion also.
I'm trying to make a light / dark theme . And I have 2 components.
The first component is the one below. I succeeded to make a boolean to change my style in my component. It was good so far :
export interface Props{
theme:boolean;
}
const Theme: FC<Props> = ({theme}) => {
const [light,setLightMode]=useState(false);
return (
<div>
<div className="card__theme">
<div className={light ? "card__light" : "card__light--active"}>Light</div>
<Switch inputProps={{ "aria-label": "controlled" }} onChange={()=>setLightMode(!light)}/>
<div className={ light ? "card__Dark" : "card__Dark--active"}>Dark</div>
</div>
</div>
);
};
What I do right now is to use this component in my Home page where I want to do the same thing.
I want to change the style of my components , making 2 className like the code above with a boolean state.
The problem that I'm facing is that I can't make another function onChange={} in my home component. First I don't send in interface a function . I want my const [light,setLightMode]=useState(false); and my function onChange={()=>setLightMode(!light)} when I switch the button to work in my Home component from below
const Home: FC = () => {
const percentage = 91;
const [dark,setDark] = useState(false);
return (
<div>2
<div className="Container-body">
<div className="card">
<div className={dark ? "card__theme" : "card__theme--active"}>
<Theme theme={dark} onChange={()=>{setDark(!dark)}}/>
</div>
So to make it sort. I want to use just the light, setlightTheme boolean state from my Theme component in my Home component . I don't know how I can send this with props in TypeScript...
Here is a print with my code : enter link description here
I'm not sure if I understand correctly, but I think your goal is to pass the onChange function to the Theme component from the parent component.
If so, I hope this snippet can help you. Basically I added the onChange parameter in the interface as a function. I also renamed the theme parameter to dark.
To make the two components work together, I removed the useState in the Theme component and used the dark variable (taken from the parent component) to manage the styles.
export interface Props {
dark: boolean;
onChange?: () => void;
}
const Theme: FC<Props> = ({ dark, onChange }) => {
return (
<div>
<div className="card__theme">
<div className={dark ? 'card__light' : 'card__light--active'}>
Light
</div>
<Switch
inputProps={{ 'aria-label': 'controlled' }}
onChange={onChange}
/>
<div className={!dark ? 'card__Dark' : 'card__Dark--active'}>Dark</div>
</div>
</div>
);
};
So if I understand correctly you just want to provide an option for switching light and dark mode. From an architectural point of view you don't want to do this on a component-level since multiple component might need to change.
BEM Mistake
Before I address a proper solution you are currently making a BEM mistake with this line:
<div className={dark ? 'card__light' : 'card__light--active'}>
A modifier --modifier can only live together with an actual element card. So on a fully outputted HTML this is valid card__light card__light--active but in your example either card__light or card__light--active is being rendered.
Solution for theme switching
When it comes down to switching a dark/light theme you either want to make this value (true or false) available in a centralised state by using something like Redux or you might want to declare it on your root component (most often the <App />) and start using it from there.
I don't recommend prop drilling for this use case since it will make your code messy in the long run. This is a really nice use case for the Context API.
✅ See working example Playground.
P.s. I use Typescript for better code, you can ignore the syntax you don't know.
React documentation
The React documentation has an exact example for you see docs theme switching.
I have a lot of components I need to get a value called "values" from the SideBarBlurChange component to the SideBar component. I drew a diagram of my components so you can navigate easier
I read articles and how I understood there are two main options, the first is "Lifting State Up" and the second is "Redux" or "Context". I tried to apply these approaches but I failed.
The main problem is that inside the SideBarBlurChange component, I cannot import anything, since my entire project is collapsing. In addition to all this, I will leave a link to this project in the github if you want to see GitHub Project
Now I want to explain my problem in more detail.
Please pay attention to the changing number I showed with the mouse this number and there is a value with the name "values". I need to apply this value to the SideBar component to adjust the blur of the Sidebar.
And finally, before I demonstrate my code, I imported the SideBar inside the SideBarBlurChange, took out a value called "values" and applied it to the SideBar component like this <div style = {{backdropFilter: blur(${props.values}px)}}...
Now look my project has collapsed, I mean that there is a catastrophe for my components, but I got "values" and everything works for me.
Now I think that the problem is understandable, in order not to confuse you, I will show you three SideBar components, SideBarBlurChange and DraggableDialog where I imported SideBarBlurChange, + delete all personal code and show only the most important thing, but if you need to look at all the other components, I will remind you that I left a link to project in github, or as a last resort, tell me I will edit my question and show what you need in advance. I want to thank you for taking the time to solve my problem
SideBar.jsx
export default function SideBar(props) {
return (
<div style={{backdropFilter: "blur(60px)"}}>
// jsx code
</div>
);
}
SideBarBlurChange.jsx
const MIN = 0;
const MAX = 100;
export default function SideBarBlurChange(props) {
const ls = parseInt(window.localStorage.getItem('values'));
const [values, SetValues] = useState(ls ? [ls] : [20]);
const SaveChanges = () => {
localStorage.setItem('values', values);
}
return (
<>
<div>
<Range
// code
/>
<output style={{ marginTop: "30px" }} id="output">
{values[0].toFixed(1)}
</output>
<button onClick={() => SaveChanges()}>Save</button>
</div>
</>
);
}
DraggableDialog.jsx
const DraggableDialog = (props) => {
return (
<>
<div>
<SideBarBlurChange {...props}/>
</div>
</>
);
};
export default DraggableDialog;
I removed a lot of code from these three components, I left only the most important. Sorry in advance, I don't know English, I wrote it all with the help of translate
I am rebuilding a project (a drag and drop website builder ) in React that used to be built with a lot of jQuery.
I need to be able to add and remove elements inside one single parent div but all I can find are methods that people say are "anti patterns" in react. Surely there is a way to have a function that can insert some predefined html into a div and have the functionality to remove it from the DOM too.
Maybe I am not thinking correctly and this needs an entirely different approach. Does anyone know how to do this?
Would using the react-jquery package allow me to SAFELY add and remove elements from the DOM? or is that a bit janky?
Yes, you can absolutely add and remove elements in react. You are looking for conditional rendering.
In JSX with the && operator
return (
{showElement && <div>Hello</div>}
)
In JSX with the ternary operator
return (
{showA ? <div>This is A</div> : <div>This is not A</div>}
)
or you can do the logic outside the return in the body of a Function Component for example:
const isLoggedIn = this.state.isLoggedIn;
let button;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
The layout of my app changes depending on some choices made by the user so the same component gets hung under different nodes in the DOM. Unfortunately, React unmounts and re-mounts the component. As a result, my component loses the state is has accumulated. I have tried to add a key property to convey the information that this is the same instance but I got the same results. The below is obviously a SSCCE. Every time the user clicks the button, component A gets unmounted and re-mounted:
class A extends React.Component {
componentWillUnmount = () => {
console.log('map::componentWillUnmount()');
}
componentDidMount = () => {
console.log('map::componentDidMount()');
}
render() {
return (
<div>
this is component A
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state={i:0};
}
increment = () => {
this.setState({i: this.state.i+1});
}
render() {
console.log('app::render');
if (this.state.i % 2 === 0)
return (
<>
<div>
<div>
<A key={42}/>
</div>
</div>
<button onClick={this.increment}>re-render</button>
</>
);
else return (
<>
<div>
<A key={42}/>
</div>
<button onClick={this.increment}>re-render</button>
</>
)
}
}
Just to clarify, my code isn't trying to achieve anything except reproduce the issue, like I said, it's a SSCCE. Surely there are apps where they user can change the layout from a "preferences" menu so that a component ends up in a different place in the DOM depending on the user's preferences. I can't imagine its acceptable to lose the state in such a situation. What is the proper way to deal with this kind of situations?
This is because the conditional rendering is returning two different tree structures where the path to A is different.
React.Fragment > div > div > A
React.Fragment > div > A
Imagine if we're mimicking React by using plain JS to do the mounting and unmounting manually, we will have to:
Store <A/> as a variable
Remove the inner <div/> (<A/> will automatically be removed as well)
Then append the previously stored <A/> into the outer <div/> (which actually is not a good approach either because this assumes <A/> will never need to update itself once it's mounted, even if the props change)
So long as there's a remove and append, it's quite equal to a React component being unmounted and then mounted again.
The code is a bit vague and I cannot tell what it's trying to achieve by having 2 <div/>s in the first condition and only 1 <div/> in the second. But perhaps you can use ternary operators to conditionally render just what you need and leave <A/> alone (and may be a bit of CSS to make the inner <div/> appear as if it is nested inside another <div/>). This way, <A/> will not unmount when <App/> changes state.
return (
<>
<div>
<A />
{condition ? // where `condition` is a boolean
<div style={{
position: 'absolute'
// and may be other styles to achieve the visual trickery
}}>
{/* your content here */}
</div>
:
null
}
</div>
<button onClick={this.increment}>re-render</button>
</>
)
By the way, they say 42 is the answer to everything, but I think not for this case. Setting the key to 42 won't help. 😅