I have code:
const H1 = ({style, children}) => {
return <h1 className={styleMap[style]}>{children}</h1>
}
const H2 = ({style, children}) => {
return <h2 className={styleMap[style]}>{children}</h2>
}
My goal is to reduce repeated code, so that I will have something like:
const H{number} = ({style, children}) => {
return <h{number} className={styleMap[style]}>{children}</h{number}>
}
But I would be able to call it like
<H1 style='style5'>Main Title</H1>
<H2 style='style2'>Subtitle</H2>
const H{number} = ({style, children}) => {
return <h{number} className={styleMap[style]}>{children}</h{number}>
}
This syntax you want is not possible. But you can dynamically decide which header level you wanna you by passing in a prop.
function Header(props) {
const { level, style, children } = props
if (Number.isInteger(level) && level >= 1 && level <= 6) {
return React.createElement(`h${level}`, { className: styleMap[style] }, children)
} else {
throw Error('Invalid "level" prop, must be number 1-6')
}
}
// Usage:
<Header level={1} style='style5'>Main Title</Header>
You have to resort to the plain JS React.createElement syntax, cus JSX doesn't allow you to use dynamic value for component, it's simply syntax error to write <{whatever} /> in JSX.
Related
<script setup lang="ts">
import { computed } from 'vue'
import type { Sizes, Colors as GlobalColors } from '#/types/global/global';
import type {MainColors} from './types'
const props = withDefaults(
defineProps<{
size?: Sizes
color?: GlobalColors
src?: string
alt?: string
width?: number
height?: number
text?: string
status?: MainColors
}>(),
{
size: 'large',
color: 'primary',
}
)
const sizes = computed(() => {
if (props.size === 'tiny') {
return ['h-4', 'w-4']
}
if (props.size === 'small') {
return ['h-6', 'w-6']
}
if (props.size === 'medium') {
return ['h-8', 'w-8']
}
if (props.size === 'big') {
return ['h-10', 'w-10']
}
if (props.size === 'large') {
return ['h-12', 'w-12']
}
if (props.size === 'huge') {
return ['h-16', 'w-16']
}
if (props.size === 'gigantic') {
return ['h-20', 'w-20']
}
else return ''
})
</script>
<template>
<div
class="
flex avatar-group [&>.avatar]:-mr-3"
:class="`
[&>.avatar]:${sizes[0]}
[&>.avatar]:${sizes[1]}`">
<slot />
</div>
</template>
If I write this:
class="[&>.avatar]:w-12 [&>.avatar]:h-12"
Then it's working just fine.
But if I pass props like 'large' or 'big' or anything less then it's only showing in styles like this:
So as far as i understand, the styles are not uploading correctly but the weirdest thing is that two of the props are working correctly that are - 'huge', 'gigantic'. And I have no idea why..
I'm creating table of content Gutenberg custom block which reactive reflect h2 text.
Outside of map(), it works. but inside not working.
Please let me know how to modify this code.
thanks.
import { registerBlockType } from "#wordpress/blocks";
import { withSelect, select } from "#wordpress/data";
registerBlockType("theme/toc", {
title: "TOC",
icon: "list-view",
category: "layout",
edit: withSelect((select) => {
return {
blocks: select("core/block-editor").getBlocks(),
};
})(Edit),
save: () => {
return null;
},
});
export function Edit(props) {
const { blocks = [] } = props;
const headings = [];
blocks.forEach((el) => {
if (!(el.name === "core/heading" && el.attributes.level === 2)) {
return;
}
headings.push(el.attributes.content);
});
return (
<div>
<p>{headings[0]}</p> // this line works
<ol>
{headings.map((h2, i) => { // not working
<li key={i}>
<a>{h2}</a>
</li>;
})}
</ol>
</div>
);
}
Simply remove the curly braces so the JSXElemnt can be returned from the map function.
<ol>
{headings.map((h2, i) =>
<li key={i}>
<a>{h2}</a>
</li>;
)}
</ol>
something else to note is it's not advised to use the element index i as the key value. you may want to use a more unique value like id from the element you're looping through.
I have a simple tab component in NEXT. Here's my component code:
const Tabs = ({ tabItems }) => {
const setActiveTab = (tabItem) => {
for (var i = 0; i < tabItems.length; i++) {
tabItems[i].active = false;
}
tabItem.active = true;
}
return <div>
{
tabItems.map(tabItem => <div
className={tabItem.active ? "active-tab" : ""}
onClick={() => setActiveTab(tabItem)}
>{tabItem.title}</div>)
}
</div>
}
However, when I click on tab items, the state does not change.
How can I change state in client-side in NEXT?
You can't change state from a parent like this. The way that you're doing, you're only changing the value of tabItem inside the children component.
You could use Context API to achieve the desired result, or just pass down a function from the parent, which would be the best if you're not passing props down too many components.
Code example:
parent.tsx:
export const Parent = () => {
const [tabItems, setTabItems] = useState()
function changeTabItems(newValue) {
setTabItems(newValue)
}
return <Child changeTabItems={changeTabItems} tabItems={tabItems}/>
}
child.tsx:
export const Child = ({changeTabItems, tabItems}) => {
function setActiveTab(tabItem) {
let aux = tabItems
for (var i = 0; i < tabItems.length; i++) {
aux[i].active = false;
}
aux.active = true;
changeTabItems({...aux})
}
}
Goal: When there is a price change,i want the price number to higlight for a few seconds with a color. I do that with the toogleClassName.
Problem: When the iteration is UP UP,or DOWN DOWN,the element already has that class and the CSS animation already ran.
JSX Code:
import React, { useEffect, useState, useRef } from "react";
import styles from "./CoinContainer.module.css";
function usePrevious(data) {
const ref = useRef();
useEffect(() => {
ref.current = data;
}, [data]);
return ref.current;
}
export default function CoinContainer({ coin, price }) {
const [priceUpdated, setPrice] = useState("");
const prevPrice = usePrevious(priceUpdated);
// Update price
useEffect(() => {
setInterval(priceUpdate, 20000);
}, []);
// Apply different flash-color style according to up$ or down$ from prev
const toggleClassName = () => {
return prevPrice > priceUpdated ? styles.redPrice : styles.greenPrice;
};
function priceUpdate() {
return fetch(
`https://api.coingecko.com/api/v3/simple/price?ids=${coin}&vs_currencies=usd`
)
.then((data) => data.json())
.then((result) => {
let key = Object.keys(result);
setPrice(result[key].usd);
});
}
return (
<div className={styles.padding}>
<h2>{coin}</h2>
{/* Here is the problem,i would like to remove the class after a few seconds,or edit the CSS code to retrigger the animation */}
<h3 className={toggleClassName()}>
{priceUpdated ? priceUpdated : price}$
</h3>
</div>
);
}
CSS Code
#keyframes upFadeBetween {
from {
color: green;
}
to {
color: white;
}
}
#keyframes downFadeBetween {
from {
color: red;
}
to {
color: white;
}
}
.redPrice {
animation: downFadeBetween 5s;
color: white;
}
.greenPrice {
animation: upFadeBetween 5s;
color: white;
}
Thanks so much for any feedback/help!
You could use another variable called e.g. reset
const [reset, setReset] = useState(false);
And then set this value at your methods using setTimeout
const toggleClassName = () => {
setTimeout(() => setReset(true), 6000);
return prevPrice > priceUpdated ? styles.redPrice : styles.greenPrice;
};
function priceUpdate() {
return fetch(
`https://api.coingecko.com/api/v3/simple/price?ids=${coin}&vs_currencies=usd`
)
.then((data) => data.json())
.then((result) => {
let key = Object.keys(result);
setReset(false);
setPrice(result[key].usd);
});
}
and finally
<h3 className={reset ? "" : toggleClassName()}>
you can do this
handleClick = event => event.target.classList.add('click-state');
or as react is js library so definitly it will support js DOM elements, well there is no doubt that it uses DOM also.
so you can do this also,
for adding class
document.getElementById("id here").classList.add("classname here");
for removing class
document.getElementById("id here").classList.remove("classname here");
OR
you can also use react States
You can use a variable that will be updated every time you trigger the fetch.
In this case, we will call it animationStyle
function CoinContainer({ coin, price }) {
const [priceUpdated, setPrice] = useState("")
const [animationStyle, setAnimationStyle] = useState("")
useEffect(() => {
setInterval(priceUpdate, 20000)
}, [])
function updateAnimationStyle(){
if (prevPrice > priceUpdated) {
setAnimationStyle("redPrice")
} else {
setAnimationStyle("greenPrice")
}
setTimeout(() => {
setAnimation("")
}, 5000)
}
function priceUpdate() {
return fetch(
`https://api.coingecko.com/api/v3/simple/price?ids=${coin}&vs_currencies=usd`
)
.then((data) => data.json())
.then((result) => {
let key = Object.keys(result)
setPrice(result[key].usd)
updateAnimationStyle()
})
}
return (
<div className={styles.padding}>
<h2>{coin}</h2>
<h3 className={animationStyle}>//you avoid doing the conditional rendering that might be a bit confusing
{priceUpdated ? priceUpdated : price}$
</h3>
</div>
)
}
Okay, so basically, the approach is to avoid too large conditional rendering inside the jsx.
the function updateAnimationStyle gets called every time the fetch is triggered.
depending on the condition, updateAnimationStyle will set redPrice or greenPrice animations to animationStyle.
the animationStyle will clean after 5 seconds through setTimeout function. "it can not be less than 5s because you have set the animation duration to 5s".
const [priceUpdated, setPrice] = useState("") it is better to avoid this type of naming for variables.
Instead, if you use const [priceUpdated, setPriceUpdated] = useState(""), it will be more readable.
Hope it works for you!
Regards.
I'm trying to integrate THEOPlayer in my project and I want to customize styles depending on certain events. For instance, I would love to hide the toolbar and show an overlay image when the video is paused.
They do expose some CSS classes that I can change manually but my question is, how do I change the values in CSS on a specific event. Since the player is imported as a single JSX element I don't know how to add custom classes to its specific parts. So I would like to know if there is another way.
Here is a component where an instance of Player is created:
class Player extends React.Component {
_player = null;
_el = React.createRef();
componentDidMount() {
const { source, onPlay, onPause } = this.props;
if (this._el.current) {
this._player = new window.THEOplayer.Player(this._el.current, {
libraryLocation:
"https://cdn.myth.theoplayer.com/7aff3fa6-f92e-45f9-a40e-1bce9911b073/",
});
this._player.source = source;
this._player.addEventListener("play", onPlay);
this._player.addEventListener("pause", onPause);
}
}
componentWillUnmount() {
if (this._player) {
this._player.destroy();
}
}
render() {
return (
<div
className={
"theoplayer-container video-js theoplayer-skin vjs-16-9 THEOplayer"
}
ref={this._el}
>
</div>
);
}
}
export default Player;
And that's a part of code where I want to change styles onPlay and onPause
<div className={"player-container"}>
<Player
source={source}
onPlay={() => {
console.log("playing");
}}
onPause={() => {
console.log("paused");
}}
/>
</div>
Use like this
state = {
play: false,
pause: true,
}
const playFn = () => {
this.setState = ({
play: true,
pause: false,
})
}
const pauseFn = () => {
this.setState = ({
play: false,
pause: true,
})
}
<div className={"player-container"}>
<Player
source={source}
onPlay={playFn}
onPause={pauseFn}
activatePlayClasses={play}
activatePauseClasses={pause}
bg={'https://example/example.jpg'}
/>
</div>
// on Player component
const { source, onPlay, onPause, activatePauseClasses, activatePlayClasses , bg} = this.props;
render() {
return (
<div
className={
`theoplayer-container video-js theoplayer-skin vjs-16-9 THEOplayer
${activatePauseClasses ? 'your pause class' : ''}
${activatePlayClasses ? 'your play class' : ''}`
}
style={{backgroundImage: `url(${bg})`}}
ref={this._el}
>
</div>
);
}
I have updated code