I am trying to add a className property to a newly created component like this:
const component = <Icons.RightArrowIcon /> // I want to add a className to this component
// Then...
// ...
return (
<>{component}</>
)
I wrote the following code:
type IconButtonProps = {
icon: 'RightArrowIcon' | 'WordIcon';
className?: string;
};
// eslint-disable-next-line react/display-name
const IconButton = React.forwardRef<any, IconButtonProps>((props, ref) => {
const getIcon = () => {
let icon: React.ReactNode | null = null
if (props.icon === 'RightArrowIcon') {
icon = <Icons.RightArrowIcon />
} else if (props.icon === 'WordIcon') {
icon = <Icons.WordIcon />
}
// This is what I thought it would be
if (icon !== null) { icon.props.className = styles.icon_button__icon }
return icon
}
return (
<div
ref={ref}
className={`${props.className} ${styles.icon_button__wrapper}`}
>
<button className={styles.icon_button}>
{getIcon()}
</button>
</div>
)
})
export default IconButton
But I got this error:
TypeError: Cannot add property className, object is not extensible
Not sure if it needs to be that complicated. Why not just render the icon conditionally?
return (
<div
ref={ref}
className={`${props.className} ${styles.icon_button__wrapper}`}
>
<button className={styles.icon_button}>
{
props.icon === 'RightArrowIcon'?//or any condition here
(
<Icons.RightArrowIcon className={style.anyClassHere}/>
):
(
<Icons.WordIcon className={style.anyClassHere}/>
)
}
</button>
</div>
)
Related
I'm trying to show lists of objects such as types and brands that I have in DyeStore my react app and after using map my app crashes but vs code doesn't tell about errors.
The following error occurs in these components:
const BrandBar = observer(() => {
const {dye} = useContext(Context)
return (
<Form className='d-flex'>
{dye.Brands.map(brand=>
<Card key={brand.id} className="mt-2" onClick={()=> dye.SetSelectedBrand(brand)} border={brand.id === dye.SetSelectedBrand.Id ? 'danger' : 'light'} style={{cursor:'poiinter'}}>
{brand.name}
</Card>
)}
</Form>
);
}
)
const TypeBar = observer(() => {
const {dye} = useContext(Context)
return (
<ListGroup>
{dye.Types.map( type =>
<ListGroup.Item
style={{cursor: 'pointer'}}
active = {type.id === dye.SetSelectedType.id }
onClick={() => dye.SetSelectedType(type)}
key={type.id}>
</ListGroup.Item>)}
</ListGroup>
);
}
)
This is the class where I store my brands and types arrays.
import { makeAutoObservable } from "mobx"
export default class DyeStore{
constructor(){
this._Types = [
{id:1,name:'Акриловая'}
]
this._Brands = [
{id:1,name:'Самсунг'}
]
this._Dyes = [
{id:1,name:'Самсунг',price: 2400,rating:5}
]
this._SelectedType = {}
this._SelectedBrand = {}
makeAutoObservable(this)
}
}
I also have setters and getters for DyeStore:
What errors I got:
I was trying to resolve this problem myself but most of the answers were not in the theme so.
Getting a build error usecallback hook called conditinally. if i dont use useCallback i get the error JSX props should not use arrow functions
const LinkComp = <T extends {}>(props: LinkProps & T extends AnchorProps ? AnchorProps : ButtonProps) => {
const {
title,
hideTitle,
children,
url = '',
action,
label,
newWindow,
className,
iconName,
isExternal,
inheritColor = true,
underlineOnHover = true,
underline = false,
theme = '',
bold = false,
onClick,
modal = false,
forceAnchorTag = false,
appendQueryParams = true,
showOutline = true,
...linkProps
} = props;
const [handleClick] = useRouter(action, url);
const forwardedParams = useSelector(selectForwardedQueryParams);
const linkContent = (
<>
{iconName && <Icon name={iconName} className='mr-3' />}
{!hideTitle && (title || children)}
</>
);
if (modal) {
if (linkProps.modalTitle) delete linkProps.modalTitle;
return (
<button {...(anchorProps as ButtonProps)} role='link' onClick={onClick as ButtonProps['onClick']}>
{linkContent}
</button>
);
}
// queryString.stringifyUrl combines the params from both url and forwarded params.
// don't append the params for `tel` links
const forwardedParamsUrl = queryString.stringifyUrl({ url, query: !url?.includes('tel:') && forwardedParams });
const handleAnchorClick = useCallback((e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
if (handleClick) handleClick(e);
return true;
},[handleClick]);
if (forceAnchorTag) {
return (
<a href={forwardedParamsUrl} {...(anchorProps as AnchorProps)} onClick={handleAnchorClick}>
{linkContent}
</a>
);
}
// search extras uses the query params in a different way, so no need to append them here
const fullUrl = appendQueryParams ? forwardedParamsUrl : url;
return (
<Link href={fullUrl} passHref={isExternal || newWindow}>
<a {...(anchorProps as AnchorProps)} onClick={handleClick}>
{linkContent}
</a>
</Link>
);
};
export default LinkComp;
You need to move your useCallback definition to above the if (model) check. This if block may cause your component to render before the function has been memoized by useCallback.
import React, { useState } from "react";
import PlanetInfoCard from "./PlanetInfoCard";
interface PlanetInfosProps {
planetName: string;
averageTemperature: string;
distanceFromSun?: string;
radius: string;
svg: string;
isDisplayed: boolean;
}
const PlanetInfos: React.FC<PlanetInfosProps> = ({
planetName,
svg,
distanceFromSun,
averageTemperature,
radius,
isDisplayed,
}) => {
const [displayStatus, setDisplayStatus] = useState(isDisplayed);
const displayPlanetInfo = () => {
setDisplayStatus(!displayStatus);
};
return (
<div
onClick={displayPlanetInfo}
key={planetName}
className="planet-list__infos"
>
<li>
<img src={svg} alt="planet-logo" /> <p>{planetName}</p>
</li>
{displayStatus && (
<PlanetInfoCard
key={distanceFromSun}
planetName={planetName}
svg={svg}
distanceFromSun={distanceFromSun}
averageTemperature={averageTemperature}
radius={radius}
isDisplayed={isDisplayed}
/>
)}
</div>
);
};
export default PlanetInfos;
In here each planet has isDisplayed value in their data object. I managed to display them individually, using useState. But I wanna hide other elements while showing one. I tried to create other states and it got messy, couldn't manage to do it.
You could have a state for deciding which element to show.
like:
[displayingItem, setDisplayingItem] = useState(oneOfThePlanetNames);
and you could display any planet you want by setDisplayinItem(planetName)
This way only one item displays each time.
{displayingItem === planetName && (
<PlanetInfoCard
key={distanceFromSun}
planetName={planetName}
svg={svg}
distanceFromSun={distanceFromSun}
averageTemperature={averageTemperature}
radius={radius}
isDisplayed={isDisplayed}
/>
)}
You should do it in the parent component where your list of PlanetInfos located.
[displayedPlanet, setDisplayedPlanet] = useState('');
const setPlanet = (name: string) => {setDisplayedPlanet(name)}
return (
<React.Fragment>
{listOfPlanets.map((planet) =>
<div onClick={setPlanet.bind(null, planet.planetName)}>
{planet.planetName === displayedPlanet && <PlanetInfos planet=planet/>}</div>)}
</React.Fragment>
)
I want to render Modal componen if data.alarmMode === EMERGENCY, however, I am not able to rendering anything and getting this error. I have tried to fix this problem but I am not able to fix this problem. I don't know why react cannot render Modal componenet.
return (
<Modal key={hash} hash={hash} data={alarmData[hash]} onClickAlarmOff={onClickAlarmOff} clockMode={clockMode} onClickCloseModal={onClickCloseModal} />
)
in case
import React, { useState } from 'react';
import CurrentTimeBox from './CurrentTimeBox';
import AlarmList from './AlarmList';
import AddAlarm from './AddAlarm';
import styles from './ClockBox.module.css';
import ModalEvent from '../containers/ModalEvent';
import { EMERGENCY, NIGHT } from '../constants';
import Modal from '../components/Modal';
import ClockModeBox from './ClockModeBox';
const ClockBox = ({ timeString, onClickCloseModal, makeModalvisible, currentTime, clockMode, modal, alarmData, onClickAlarmOn, onClickAlarmOff, ClockModeFunctionLists }) => {
return (
<div className={styles.ClockBox}>
<CurrentTimeBox currentTime={currentTime} />
<ClockModeBox ClockModeFunctionLists={ClockModeFunctionLists} clockMode={clockMode}/>
<div className={styles.AlarmLists}>
{Object.keys(alarmData).length > 1 && alarmData.dataLists.map((hash) =>{
const data = alarmData[hash]
return (
<AlarmList key={hash} hash={hash} data={data} onClickAlarmOn={onClickAlarmOn} onClickAlarmOff={onClickAlarmOff}/>
)
})
}
</div>
<AddAlarm />
{timeString.length > 0 && Object.keys(alarmData).length > 1 ? alarmData.dataLists.map((hash) => {
const data = alarmData[hash]
if (data.time === timeString && data.on) {
if (clockMode === NIGHT) {
if (data.alarmMode === EMERGENCY) {
makeModalvisible();
return (
<Modal key={hash} hash={hash} data={alarmData[hash]} onClickAlarmOff={onClickAlarmOff} clockMode={clockMode} onClickCloseModal={onClickCloseModal} />
)
} else {
return null;
}
}
makeModalvisible();
// if (modal) {
return (
<Modal key={hash} hash={hash} data={alarmData[hash]} onClickAlarmOff={onClickAlarmOff} clockMode={clockMode} onClickCloseModal={onClickCloseModal} />
)
// }
}
}) : null
}
</div>
)
};
export default ClockBox;
updated code
return (
<div className={styles.ClockBox}>
<CurrentTimeBox currentTime={currentTime} />
<ClockModeBox ClockModeFunctionLists={ClockModeFunctionLists} clockMode={clockMode}/>
<div className={styles.AlarmLists}>
{Object.keys(alarmData).length > 1 && alarmData.dataLists.map((hash) =>{
const data = alarmData[hash]
return (
<AlarmList key={hash} hash={hash} data={data} onClickAlarmOn={onClickAlarmOn} onClickAlarmOff={onClickAlarmOff}/>
)
})
}
</div>
<AddAlarm />
{timeString.length > 0 && Object.keys(alarmData).length > 1 ? alarmData.dataLists.map((hash) => {
const data = alarmData[hash]
if (data.time === timeString && data.on) {
debugger;
if (clockMode === NIGHT) {
if (data.alarmMode === EMERGENCY) {
makeModalvisible();
return (
<Modal key={hash} hash={hash} data={alarmData[hash]} onClickAlarmOff={onClickAlarmOff} clockMode={clockMode} onClickCloseModal={onClickCloseModal} />
)
} else {
return null;
}
}
makeModalvisible();
// if (modal) {
return (
<Modal key={hash} hash={hash} data={alarmData[hash]} onClickAlarmOff={onClickAlarmOff} clockMode={clockMode} onClickCloseModal={onClickCloseModal} />
)
// }
} else {
return null;
}
}) : null
}
</div>
)
modal component
import React from 'react';
import { VIBRATION, NORMAL, NIGHT } from '../constants';
import styles from "./Modal.module.css";
const Modal = ({ onClickCloseModal, data, clockMode, hash, onClickAlarmOff }) => {
const SOUND_EFFECT = 'https://pp.netclipart.com/pp/s/35-354079_jpg-transparent-library-hotel-front-desk-service-bell.png';
const VIBRATION_EFFECT = 'https://st2.depositphotos.com/4520249/7558/v/950/depositphotos_75586693-stock-illustration-vibration-mode-phone-icon.jpg';
let imgComponent = null;
switch(clockMode) {
case NORMAL:
imgComponent = <img src={SOUND_EFFECT} />
break;
case VIBRATION:
imgComponent = <img width="200px" height="200px" src={VIBRATION_EFFECT} />
break;
default:
return;
}
return (
<div className={styles.Modal} >
<div className={styles.ModalOverLay}></div>
<div className={styles.ModalContent}>
{imgComponent}
<p>{data.label}</p>
<button className={styles.CloseButton} onClick={() => {
onClickCloseModal();
onClickAlarmOff(hash);
console.log(33333)
}}>close</button>
</div>
</div>
)
}
export default Modal;
If the clockMode is not 'emergency the component do render Modal component correctly. However, what I want to do is render Modal component in case if (data.alarmMode === EMERGENCY)
I don't know why it cannot render in this condition even if I used return statement.
update
const modalOn = () => ({ type: MODAL_ON });
makeModalvisible: () => dispatch(modalOn())
there was no problem with mpodalOn, I checked modal status was changed
I debugged and debugger went inside of if (data.alarmMode === EMERGENCY)
the problem is when ClockBox component can't render Modal component....
if if I used return statement
I think this could be because you do not have an else statement after the following if statement: if (data.time === timeString && data.on).
So when this requirement is not met, React will not know what to render.
i have this breadcrump component that map over props and renders a list of chip components like this:
class BreadCrumb extends React.Component {
render () {
const {
steps,
activeIndex
} = this.props;
const chips = steps
.map((step,index) => {
return <Chip
key={index}
title={step.category}
onClick = {()=> this.props.selectChip(index)} // this should be passed only if
// active == true
active={activeIndex >= index} />
})
return (
<div className="chip-container">
{chips}
</div>
)
}
}
i need to click on chips only if his active prop is true,
this is the chip component
class Chip extends React.Component {
render(){
const {
active,
title
} = this.props;
const activeClassName = active ? 'chip active' : 'chip';
return (
<div
className = {activeClassName}
onClick = {() => this.props.onClick()} >
<span>{title}</span>
</div>
)
}
}
how can i make chip clickable only if the active prop is true?
For further information selectChip() function sets the state of a component App, parent of Breadcrump component, so it is binded to App component.
You could e.g. make that onClick function as a class method and use a simple condition inside:
class Chip extends React.Component {
handleClick = () => {
if (this.props.active) {
this.props.onClick(); // call only if active props is true
}
}
render() {
const { active, title } = this.props;
const activeClassName = active ? 'chip active' : 'chip';
return (
<div
className = {activeClassName}
onClick = {this.handleClick}
>
<span>{title}</span>
</div>
)
}
}
Either execute the handler or an empty function
onClick = {isActive ? this.props.onClick : () =>{} } >
You can do it like this:-
// If chip component expects a function all the time
<Chip
key={index}
title={step.category}
onClick = {step.active ? ()=> this.props.selectChip(index) : () => {}}
active={activeIndex >= index} />
// If onClick is an optional prop to chip component
<Chip
key={index}
title={step.category}
onClick = {step.active ? ()=> this.props.selectChip(index) : undefined}
active={activeIndex >= index} />
// of onClick handler is optional, possibly an alternative solution
type ChipProps = {
title: string;
active: boolean;
onClick?: ()=>void;
}
<Chip
key={index}
title={step.category}
active={activeIndex >= index}
{...(step.active ? {onClick:()=> this.props.selectChip(index)} : {})}
/>