Color-Component that receives colors throw props and does color effect.
export enum Colors {
Blue = "333CFF",
Yellow = "FCFF33",
Green = "33FF36",
}
interface IColorProps {
myColors: Colors ;
}
export const ColorComponent: FunctionComponent<IColorProps > = ( {props: IColorProps } => {
return (
<div className={`colorCalss ${props.bgColor}`} />
)});
ParentComponent that sends props to color-component
import { ColorComponent, Colors } from "ColorComponent.component";
export const ParentComponent: FunctionComponent<IParentComponentProps> =
(props: IParentComponentProps) => {
<ColorComponent myColors={Colors.Blue}/>
}
The below is what I have placed into the IParentComponentProps
export enum Colors {
Blue = "333CFF",
Yellow = "FCFF33",
Green = "33FF36",
}
interface IColorProps {
myColors?: Colors ;
}
export interface IParentComponentProps {
colors: IColorProps;
}
I just started working with typescript.
The above Parent component sends props myColors to ColorComponent and its working I can see blue color being apply on my tests.
How can I send props dynamically to ColorComponent, for example lets say the parent.component props.color get different color that could be apply.
When I do something like: "<"ColorComponent myColors={props.colors}/">", I get error.
How can I get props pass down dynamically using enum?
Your parent interface should have the same color type:
export interface IParentComponentProps {
colors: Colors;
}
This way you will not get the type errors and can pass down the colors prop.
Or pass down the props.colors.myColors variable. But be careful it may not exists as you have it optional.
You don't need to do anything special to send the colors dynamically to the child. Specifically for typescript just make sure all types are consistent.
There's a couple of syntax errors on your example so maybe it's just not compiling for you because of that?
Here's a working sample where parent component dynamically changes the color and sends it to the child every second.
export enum Colors {
Blue = "333CFF",
Yellow = "FCFF33",
Green = "33FF36",
}
interface IColorProps {
color: Colors;
}
export const ColorComponent: React.FunctionComponent<IColorProps> = (props: IColorProps) => {
return <div style={{ backgroundColor: `#${props.color}`, height: '100px', width: '100px' }} />
};
const ParentComponent: React.FC = () => {
const colors = [Colors.Blue, Colors.Green, Colors.Yellow];
const [color, setColor] = React.useState(0);
const { current } = React.useRef({ next: function() { } });
current.next = () => {
const nextColor = (color === 2) ? 0 : (color + 1);
setColor(nextColor);
};
useEffect(
() => {
const interval = setInterval(() => current.next(), 1000);
return () => clearInterval(interval);
},
[]
);
return (
<>
<h1>Current color is #{colors[color]}</h1>
<ColorComponent color={colors[color]}/>
</>
);
}
Related
The Material UI documentation has a section about "Consecutive Snackbars", but does not indicate how we could extract the logic to a generic component so we could use it in several places of our application.
I'm using React v18 and Material UI v5.
A working example is to replace the "click" event handler of the MUI documentation (i.e. handleClick) with an effect depending on a passed prop (i.e. content, renamed value):
// src/Snack.tsx
import { Snackbar, SnackbarProps } from "#mui/material";
import React from "react";
type SnackContent = {
key: number;
value: React.ReactNode;
};
// Omit all props necessary for the consecutive snackbars logic
type SnackProps = Omit<
SnackbarProps,
"children" | "key" | "message" | "onClose" | "open" | "TransitionProps"
> & {
content: SnackContent["value"];
};
export const Snack: React.FC<SnackProps> = ({
// Passed `content` is actually the `SnackContent.value`
content: value,
...otherProps
}) => {
const [content, setContent] = React.useState<SnackContent>();
const [pack, setPack] = React.useState<readonly SnackContent[]>([]);
const [isOpen, setIsOpen] = React.useState<boolean>(false);
const handleSnackClose = (
event: React.SyntheticEvent | Event,
reason?: string
) => reason !== "clickaway" && setIsOpen(false);
const handleSnackExited = () => setContent(undefined);
// Update content pack
React.useEffect(() => {
value && setPack((prev) => [...prev, { key: new Date().getTime(), value }]);
}, [value]);
// Handle consecutive snackbars https://mui.com/material-ui/react-snackbar/#consecutive-snackbars
React.useEffect(() => {
if (pack.length && !content) {
// Set a new snack when we don't have an active one
setContent({ ...pack[0] });
setPack((prev) => prev.slice(1));
setIsOpen(true);
} else if (pack.length && content && isOpen) {
// Close an active snack when a new one is added
setIsOpen(false);
}
}, [pack, content, isOpen]);
return (
<Snackbar
key={content?.key}
open={isOpen}
autoHideDuration={6000}
onClose={handleSnackClose}
TransitionProps={{ onExited: handleSnackExited }}
{...otherProps}
>
{/* A "div" wrapper is required so `content.value` can be `null` */}
<div>{content?.value}</div>
</Snackbar>
);
};
Usage:
// src/SomeComponent.tsx
import React from "react";
import { Snack } from "./Snack";
export const SomeComponent: React.FC = () => {
const [snackContent, setSnackContent] = React.useState<React.ReactNode>();
// The "hello world" text is wrapped with React.Fragment so the `Snack` component rerenders when its `content` prop value changes
const handleTestClick = () => setSnackContent(<>"Hello, world!"</>);
return (
<>
<button onClick={handleTestClick}>Test</button>
<Snack
content={snackContent}
anchorOrigin={{ horizontal: "center", vertical: "bottom" }}
/>
</>
);
};
This works both on classic and touch desktops.
Here is a code sandbox.
I want to add dark mode in my React Native App. I have come across many implementations including React Navigation and Appearance. Everywhere I see code similar to the one below:-
import { Text, useColorScheme } from 'react-native';
const theme = useColorScheme();
<Text style={theme === 'light' ? styles.text_light : styles.text_dark}>Hello</Text>
But using these ternary operators everywhere is adding up to noise in code. I have created my custom Text and View as you can see below.
import { Text, useColorScheme } from 'react-native';
const theme = useColorScheme();
const CustomText = ({ styles, children }) => {
return (
<Text
style={[
theme === 'dark' ? { color: 'white' } : { color: 'black' },
styles,
]}>
{children}
</Text>
);
};
My Question is, Is my solution feasible? Can I replace React Native Components like Text and View(that get used everywhere) with custom components, or the ternary operator in React Components a better way. Thank you.
react-native-paper provides components that are colored using the theme stored in the PaperProvider context. React navigation has a similar context. Since these themes are compatible with one another, here's an example where users allow to manipulate both:
First merge the paper and navigation light and dark schemes:
// React native paper theme is compatible with react-navigation theme
// so merge them
const CombinedDefaultTheme = merge(
PaperDefaultTheme,
NavigationDefaultTheme
);
const CombinedDarkTheme = merge(PaperDarkTheme, NavigationDarkTheme);
Now I create a function that will customize the merged scheme base on a provided color and whether or not dark mode is desired:
// create theme base on provided color and whether in darkmode
export const getTheme = (color, isDarkMode) => {
// use color scheme generator to get colors
const [primary, accent, ...otherColors] =
colorSchemes.getNeutralScheme(color);
const colorPresets = isDarkMode
? darkenColorPresets
: lightenColorPresets;
// get lighter and darker versions of these colors
const primaryPresets = colorPresets(primary);
const accentPresets = colorPresets(accent);
// get default paper& navigation theme
const DefaultTheme = isDarkMode
? CombinedDarkTheme
: CombinedDefaultTheme;
//override theme with changes
const theme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
primary,
accent,
primaryPresets,
accentPresets,
otherColors,
},
};
// make background color darker
const onDark = {
background: colorManipulators.blend(primary, "#000", 0.8),
};
// make text lighter version of background color
onDark.text = colorManipulators.blend(onDark.background, "#fff", 0.6);
if (isDarkMode) theme.colors = { ...theme.colors, ...onDark };
return theme;
};
And now put this theme into a piece of state with useReducer:
import React, { useReducer } from "react";
import { useColorScheme } from "react-native";
import { getTheme } from "../colors";
const initialState = {
mainColor: "#0D3FF8",
};
initialState.isDarkMode = false;
initialState.theme = getTheme(
initialState.mainColor,
initialState.isDarkMode
);
const reducer = (state, action) => {
switch (action.type) {
case "setMainColor": {
let newColor = action.payload;
return {
...state,
mainColor: newColor,
theme: getTheme(newColor, state.isDarkMode),
};
}
case "overrideTheme": {
let newTheme = action.payload;
return { ...state, theme: { ...state.theme, ...newTheme } };
}
case "setIsDark": {
const newDark = action.payload;
return {
...state,
isDarkMode: newDark,
theme: getTheme(state.mainColor, newDark),
};
}
}
};
export default function ThemeReducer(stateOverride = {}) {
console.log("here is the state override");
console.log(stateOverride);
const [state, dispatch] = useReducer(reducer, {
...initialState,
isDarkMode: useColorScheme() == "dark",
...stateOverride,
});
return { ...state, dispatch };
}
I'm creating a web app that serves the user unique pages with a substantial amount of JSX e.g.
<Explanation>
<p>Here will be a lengthy section explaining how to solve a problem that I want rendered using the explanation component</p>
<Quote>It might also have child components in it</Quote>
</Explanation>
<Questions>Here will be some questions I've written</Questions>
<Image> Here will be an image that I want to render using my image component</Image>
Ideally I would like to store the JSX in a mongo database as an object like this:
post = {
_id: 1,
content: `<Explanation>
<p>Here will be a lengthy section explaining how to solve a problem that I want rendered using the explanation component</p>
<Quote>It might also have child components in it</Quote>
</Explanation>
<Questions>Here will be some questions I've written</Questions>
<Image> Here will be an image that I want to render using my image component</Image>`
because I will have many hundreds of such pages and the user should receive only a particular page.
Is there a way I can achieve this?
At the moment I have resigned to writing an object like this:
post = {
_id,
content: [
{component: Explanation,
props: {key: 'bla'},
content: [
{component: p,
props: null,
content: 'Here will be a lengthy section explaining how to solve a problem that I want rendered using the explanation component'
}
]
}
]
and then writing a function that turns them into React elements like so:
const renderPost = (post) => {
const JSX = post.content.map(c =>
React.createElement(c.component, c.props, c.content)
)
return JSX
}
but this whole process feels cumbersome and inefficient.
Is there a better way of trying to achieve my goal?
This is a quick work around.
import React from "react";
import "./styles.css";
var str = `
<Explanation>
<p>this is ex1 p tag</p>
<Quote>this is quote</Quote>
</Explanation>
<Questions>This is q</Questions>
<Image>this is img</Image>
`;
const createMarkup = (props) => ({ __html: props });
const Custom = ({ children }) => {
return <div dangerouslySetInnerHTML={createMarkup(children)} />;
};
const Quote = ({ children }) => {
return <blockquote dangerouslySetInnerHTML={createMarkup(children)} />;
};
const Questions = ({ children }) => {
return <q dangerouslySetInnerHTML={createMarkup(children)} />;
};
const Image = ({ children }) => {
return <h1 dangerouslySetInnerHTML={createMarkup(children)} />;
};
const Explanation = ({ children }) => {
return (
<div className="explain" dangerouslySetInnerHTML={createMarkup(children)} />
);
};
const cpmponentToShow = [];
const components = {
Custom,
Explanation,
Image,
Questions,
Quote
};
const componetCreate = (string) => {
const tags = string.match(/(?<=<)((\w|\b)+)[^>]/g);
if (tags.length) {
tags.map((tag) => {
var regexNew = `<${tag}>([\\s\\S]*?)</${tag}>`;
var matched = string.match(regexNew);
const leftOver = matched[1].match(/(?<=<)(\w+)[^>]/g);
if (!leftOver) {
cpmponentToShow.push(
React.createElement(
components[tag] ? components[tag] : tag,
null,
matched[1]
)
);
}
return null;
});
}
};
componetCreate(str);
export default function App() {
return (
<div className="App">
{cpmponentToShow.map((item, index) => (
<React.Fragment key={index}>{item}</React.Fragment>
))}
</div>
);
}
p {
color: red;
}
blockquote {
color: black;
}
q {
color: purple;
}
h1 {
color: royalblue;
}
.explain {
color: salmon;
}
sandbox link
i want to check if an element is present in the dom and add style to another element in the dom using react.
What i am trying to do?
i have a popup and a button displayed in same page. so when the button is present i want to add a space of 16px between the popup and button.
below is the snippet,
const Root = () => {
<PopupContextProvider>
<App/>
</PopupContextProvider/>
}
function App() {
return (
<Items/>
<Drawer/>
);
}
function Items() {
const [isOpen, setIsOpen] = React.useState(openByDefault);
const availableItems = 10;
useItemsTrigger(isOpen, availableItems);
}
function Drawer() {
return (
<DrawerButton/> //here is where the DrawerButton is called
);
}
function DrawerButton({ onClick, active, disabled }: Props) {
return (
<ButtonElement //this is the button
onClick={onClick}
data-testid="drawer-button"
/>
);
}
const ButtonElement = styled(IconButton)`
position: fixed;
left: ${-toggleWidthPx - toggleGutterPx}px;
bottom: ${toggleGutterPx}px;
width: ${toggleWidthPx}px;
height: ${toggleWidthPx}px;
padding: 0;
& > svg {
margin: 0;
}
`;
interface ContextProps {
triggerForItems: (availableItems: number) => void;
triggerForProducts: (availableProducts: number) => void;
}
const popupContext = React.createContext<ContextProps>({
triggerForItems: (availableItems: number) => {},
triggerForProducts: (availableProducts: number) => {},
});
const usePopupContext = () => React.useContext(popupContext);
export const PopupContextProvider = ({ children }: any) => {
const [showForItems, setShowForItems] = React.useState(false);
const [showForProducts, setShowForProducts] = React.useState(false);
const dismiss = () => {
if (showForItems) {
sessionStorage.setItem(itemsPopupId, 'dismissed');
setShowForItems(false);
}
if (showForProducts) {
sessionStorage.setItem(mobileUsersPopupId, 'dismissed');
setShowForProducts(false);
}
};
const isDismissed = (dialogId: string) => {
sessionStorage.getItem(dialogId) === 'dismissed';
const context = {
triggerForItems: (availableItems: number) => {
if (!isDismissed(itemsPopupId) && availableItems <= limit) {
setShowForItems(true);
} else if (availableitems > limit) {
setShowForItems(false);
}
},
triggerForProducts: (availableProducts: number) => {
if (!isDismissed(productsPopupId) && availableProducts <= limit) {
setShowForProducts(true);
} else if (availableProducts > limit) {
setShowForProducts(false);
}
},
};
return (
<popupContext.Provider value={context}>
{children}
{(showForSiteShares || showForMobileUsers) && (
<Popup onHide={dismiss} />
)}
</popupContext.Provider>
);
};
export function useProductsTrigger(
enabled: boolean,
availableProducts: number
) {
const { triggerForProducts } = usePopupContext();
React.useEffect(() => {
if (enabled) {
triggerForProducts(availableProducts);
}
}, [enabled, availableProducts, triggerForProducts]);
}
export function useItemsTrigger(
enabled: boolean,
availableItems: number
) {
const { triggerForItems } = usePopupContext();
React.useEffect(() => {
if (enabled) {
triggerForItems(availableItems);
}
}, [enabled, availableItems, triggerForItems]);
export function Popup() { //this is the popup
return (
<Dialog>
<Body>
<span>Title</span>
<Description/>
</Body>
<Actions>
<span> Hide</span>
</Actions>
</Dialog>
);
}
How can i check if the button (ButtonElement) is present in DOM and add margin-left to popup (Dialog).
i can add id to ButtonElement and check if the element is present. but do i add style to Popup if element is present?
Also can i use ref instead of adding id to ButtonElement? If so how to do it?
Or is there any other better way to do this?
could someone help me solve this. thanks.
You should lift the state up.
The source of truth should live in your state, not the DOM.
The popup is rendered due to some condition, that condition should be stored in state and then shared with the rest of the application. You can use the Context API to do so.
Then in your button component you can query that state and adjust the style accordingly.
So this setup should look something like this:
const PopupContext = createContext()
const App() {
const [popupVisible, setPopoupVisible] = useState(false)
<PopupContext.Provider value={{ popupVisible, setPopupVisible }}>
/* the rest of you application code, any component can set or use the context values */
</PopupContext.Provider>
}
const DrawerButton() {
const { popupVisible } = useContext(PopupContext)
return ... // draw button with customized styles
}
Make sure that the popup is shown if and only if the popupVisible state is true.
EDIT (OP updated the question):
You can add the visibility state to your context -
interface ContextProps {
triggerForItems: (availableItems: number) => void;
triggerForProducts: (availableProducts: number) => void;
isPopupVisible: boolean;
}
const context = {
isPopupVisible: showForItems || showForProducts,
triggerForItems: (availableItems: number) => {
if (!isDismissed(itemsPopupId) && availableItems <= limit) {
setShowForItems(true);
} else if (availableitems > limit) {
setShowForItems(false);
}
},
triggerForProducts: (availableProducts: number) => {
if (!isDismissed(productsPopupId) && availableProducts <= limit) {
setShowForProducts(true);
} else if (availableProducts > limit) {
setShowForProducts(false);
}
},
};
I am working on a task where I need to change the color of each word in a sentence or paragraph in react code for every 1 second.
Here is my code:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
colors: ["red", "yellow", "blue", "green", "purple", "pink"]
};
this.changeBg = this.changeBg.bind(this);
}
componentDidMount() {
setInterval(this.changeBg, 1000);
}
changeBg() {
const { colors } = this.state;
const color = colors[Math.floor(Math.random() * colors.length)];
document.body.style.backgroundColor = color;
}
render() {
return (
<div>
This is a sample message in react template
</div>
);
}
}
export default App;
This works fine in changing the background of the content. But my task is to change each word color randomly for every 1 second.
Example for text This is a sample message in react template
This should have one color, is should have another color, similarly all words. How can I do this?
Now these colors should change again every 1 second.
Split the sentence on word breaks (spaces) and then apply the randomly chosen color as a style for each word.
import React, { Component } from "react";
import ReactDOM from "react-dom";
class App extends Component {
constructor(props) {
super(props);
this.state = {
colors: ["red", "yellow", "blue", "green", "purple", "pink"],
sampleMessage: "This is a sample message in react template",
refresh: 0
};
}
refresh() {
let { refresh } = this.state;
refresh = refresh + 1;
this.setState({ refresh });
}
componentDidMount() {
setInterval(this.refresh.bind(this), 1000);
}
render() {
const { sampleMessage, colors, refreshRate } = this.state;
const randomColor = () => {
return colors[(Math.random() * colors.length) >> 0];
};
return (
<div>
{sampleMessage.split(" ").map(word => {
return <span style={{ color: randomColor() }}>{`${word} `}</span>;
})}
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
see it working here
https://codesandbox.io/s/random-word-colors-lu5ew
You can create a custom component for reuse and replace the html elements to your style needs.
Use case:
<ColorPara>Hello world</ColorPara>
Here's an example:
const getRandomColor = () => {
const colors = ['red', 'orange', 'green', 'blue']
return colors[Math.floor(Math.random() * colors.length)];
}
const ColorPara = (props) => {
return (
<p>
{props.children.split(' ').map(text => {
return (
<p style={{ color: getRandomColor(), display: 'inline', }}>
{text}
</p>
)
})}
</p>
)
}
function App() {
//For changing on interval
//-------------------------
const [count, setCount ] = useState(0)
setInterval(() => {
let newCount = count + 1
setCount(newCount)
}, 2000);
//--------------------------
return (
<div className="App">
<ColorPara>This is something</ColorPara>
</div >
);
}
edit: added image, added description above block of code, added gif, added color change on interval
For each word to have a separate color, it would need to be contained in a discrete element that is able to be individually targeted by CSS.
<div>This is a message</div>
Can only have one CSS rule defining the text color,
<span>This</span><span>is</span><span>better</span>
can have one for each element.
I'd recommend storing your text in state, splitting it into words like any other string, and iterating over the resulting array to render separate elements that you can style as you see fit.
Not sure what you mean exactly by "change randomly," but I tried to make the most basic example I could here