I'm trying to add an active class on the map element with hover. Everything is perfect but I need to add an active class on the first div when I do not hover over any.
Here is my code...
{WhatWeOfferData.map(({ img, title, para}, index) => {
return (
<div
className={`${style.card} ${addActive.index === index && addActive.show ? `${style.active}` : ""}`}
onMouseEnter={hoverOn}
onMouseLeave={hoverOff}
key={index}
data-id={index}
>
<Image src={img} alt="offer_images" width={37} height={41} />
<h2>{title}</h2>
<p>{para}</p>
</div>
);
})}
and
let [addActive, setAddActive] = useState(false);
const hoverOn = (e) => {
setAddActive({
index: parseInt(e.currentTarget.dataset.id),
show: true
});
};
const hoverOff = (e) => {
setAddActive({
index: parseInt(e.currentTarget.dataset.id),
show: false
});
};
Simply do this:
{
WhatWeOfferData.map(({ img, title, para}, index) => {
return (
<div className={`${style.card} ${addActive.index === index && addActive.show ? `${style.active}` : index === 0 && !addActive.show ? `${style.active}` : ""}`}
onMouseEnter={hoverOn}
onMouseLeave={hoverOff}
key={index}
data-id={index}
>
<Image src={img} alt="offer_images" width={37} height={41} />
<h2>{title}</h2>
<p>{para}</p>
</div>
);
})}
Related
I am trying to make history by pushing on button click
onclick function of the li is not working
as u can see in the code <SuggestionsList/> is in the last return statement its funtions are rendered in const SuggestionsList = (props) => { . The onclick funtion is comimg inside the const SuggestionsList funtion, this is making the onclick funtion not working
i created the exact working in codesand and its working there without any problem i dont get why its not working in my local
enter link description here
function finddoctor(e) {
console.log(e);
history.push(`/detiled/${e} `);
}
const onChange = (event) => {
const value = event.target.value;
setInputValue(value);
setShowResults(false);
const filteredSuggestions = suggestions.filter(
(suggestion) =>
suggestion.firstname
.toString()
.toLowerCase()
.includes(value.toLowerCase()) ||
suggestion.id.toString().toLowerCase().includes(value.toLowerCase())
);
setFilteredSuggestions(filteredSuggestions);
setDisplaySuggestions(true);
};
const onSelectSuggestion = (index) => {
setSelectedSuggestion(index);
setInputValue(filteredSuggestions[index]);
setFilteredSuggestions([]);
setDisplaySuggestions(false);
};
const SuggestionsList = (props) => {
const {
suggestions,
inputValue,
onSelectSuggestion,
displaySuggestions,
selectedSuggestion,
} = props;
if (inputValue && displaySuggestions) {
if (suggestions.length > 0) {
return (
<ul className="suggestions-list" style={styles.ulstyle}>
{suggestions.map((suggestion, index) => {
const isSelected = selectedSuggestion === index;
const classname = `suggestion ${isSelected ? "selected" : ""}`;
return (
<>
<li
style={styles.listyle}
onClick={finddoctor(suggestion.id)}
key={index}
className={classname}
>
{suggestion.firstname}
</li>
</>
);
})}
</ul>
);
} else {
return <div>No suggestions available...</div>;
}
}
return <></>;
};
useEffect(() => {
axios
.get("admin-panel/all-doctors-list/")
.then((res) => {
const data = res.data;
setShowSerch(data);
});
}, []);
return (
<>
<div className="note-container" style={styles.card}>
<div style={styles.inner}>
<p style={{ textAlign: "left" }}>Search Doctors</p>
<form className="search-form" style={{}}>
{showResults ? (
<FontAwesomeIcon
style={{ marginRight: "-23px" }}
icon={faSearch}
/>
) : null}
<input
onChange={onChange}
value={inputValue}
style={styles.input}
type="Search"
/>
<SuggestionsList
inputValue={inputValue}
selectedSuggestion={selectedSuggestion}
onSelectSuggestion={onSelectSuggestion}
displaySuggestions={displaySuggestions}
suggestions={filteredSuggestions}
/>
</form>
</div>
</div>
</>
);
};
Instead of onClick={finddoctor(suggestion.id)} (Here just function invocation is happening and expected to have the callback method)
should be
onClick={() => finddoctor(suggestion.id)}
So I'm having this accordion component, which currently opens and close on click. But when I click on e.g. first item and it opens, when I click on second item the first close while I want to keep it open. Not sure how should I approach this.
const [activeTab, setActiveTab] = React.useState(props.tabIndex);
const tabs = props.tabs.map((tab) => {
return (
<>
<TabContainer>
<Tab
key={tab.index}
onClick= {() => {activTab == tab.index ? setActiveTab(-1) : setActiveTab(tab.index)})
className={typeof tab.content === 'string' ? '' : 'unactive-tab'}
>
{tab.name}
</Tab>
</TabContainer>
{tab.index === activeTab ?
<div
id="content"
style={{ width: '100%', margin: '2rem 0' }}
dangerouslySetInnerHTML={{ __html: tab.content as string }}
/>
: null}
</>
);
});
The best approach is to separate the Accordion item in its own component based on what you want and each of them would be able to have a "opened" state.
I won't give you a full solution, but I would do something similar to this:
const AccordionItem = (props) => {
// Each item has it's own state.
const [isOpened, setIsOpened] = React.useState(false);
return (YOUR COMPONENT);
};
And in your main component, you just do this:
const tabs = props.tabz.map((tab) => {
return (
<AccordionItem {...tab} /≥
);
});
change props.tabIndex to be an array of tabs indexes rather than just 1 index.
Assuming you now send an array of indexes through props. e.g
// props.tabIndexes = [1,5,2]
const [activeTabs, setActiveTabs] = React.useState(props.tabIndexes);
const tabs = props.tabs.map((tab) => {
return (
<>
<TabContainer>
<Tab
key={tab.index}
onClick= {() => {activeTabs.includes(tab.index) ? setActiveTabs(activeTabs.filter(tabIndex => tabIndex !== tab.index ) ) : setActiveTabs(activeTabs.push(tab.index)})
className={typeof tab.content === 'string' ? '' : 'unactive-tab'}
>
{tab.name}
</Tab>
</TabContainer>
{activeTabs.includes(tab.index) ?
<div
id="content"
style={{ width: '100%', margin: '2rem 0' }}
dangerouslySetInnerHTML={{ __html: tab.content as string }}
/>
: null}
</>
);
});
The problem is I have two redux stores
items(Items Store
quotationItems(Quote Items.
When a product item is added to quotationItems I Would to show <RedButton title="Remove" /> or when the quotationItems is empty the <AddButton title="Add" /> should be shown.
using if statement tends to re-render the component and adds new components e.g: After adding a new product to quotation items, there will be a new <AddButton /> in FlatList Component.
interface IProps {
items: ItemInterface[];
documentItems: ItemInterface[];
onAddItem: any;
}
const ItemFlatList2: FC<Partial<IProps>> = ({
items,
documentItems,
onAddItem,
}) => {
const TestView = (itemCode) => {
documentItems.map((x) => {});
};
return (
<FlatList
data={items}
keyExtractor={(item) => item.itemCode.toString()}
renderItem={({
item: { itemCode, itemDescription, itemSellingPrice },
}) => {
return (
<div className={styles.itemContainer}>
<h4>Item Code: {itemCode}</h4>
<h4>Description: {itemDescription}</h4>
<div>
{documentItems.map((x) => {
x.itemCode === itemCode ? (
<RedButton title={"Remove"} key={itemCode} />
) : (
<AddButton title={"Add"} key={itemCode} />
);
})}
</div>
</div>
);
}}
/>
);
};
Other options I've tried:
I've also tried this, still doesn't work, the buttons will show up but after clicking on the first button, which will successful add the product item to quotation item store but when I try and click on the 2nd or 3rd button of `<AddButton title={'Add'} key={itemCode} /> I get no response.
<div>
{documentItems.length <= 0 ? (
<AddButton
title={"Add"}
key={itemCode}
onClick={() => onAddItem(itemCode)}
/>
) : (
documentItems!.map((x) =>
x.itemCode === itemCode ? (
<RedButton title={"Remove"} key={itemCode} />
) : (
<
AddButton title={"Add"} key={itemCode} onClick={() => onAddItem(itemCode)} />
)
)
)}
</div>
This is the result of the modified code above:
Please add the return keyword.
documentItems.map((x) => {
return (x.itemCode === itemCode ? (
<RedButton title={"Remove"} key={itemCode} />
) : (
<AddButton title={"Add"} key={itemCode} />
);
)})
Or Just remove the curly braces.
documentItems.map((x) => (x.itemCode === itemCode ? (
<RedButton title={"Remove"} key={itemCode} />
) : (
<AddButton title={"Add"} key={itemCode} />
)))
Based on the updations that you did in the questions. It is an issue with the data in "documentItems". Since you only need 1 button per item code. Instead of map use find. It will return 1 item and fix this duplicacy.
I currently have this bit of code, that is working, but it is throwing a Warning: Each child in a list should have a unique "key" prop.
Check the render method of `TopMenuDropdown`. See https://reactjs.org/link/warning-keys for more information.
MenuItem#http://localhost:3000/protis-react/static/js/vendors~main.chunk.js:2815:110
TopMenuDropdown#http://localhost:3000/protis-react/static/js/main.chunk.js:2432:5
div
div
App#http://localhost:3000/protis-react/static/js/main.chunk.js:63:5
Router#http://localhost:3000/protis-react/static/js/vendors~main.chunk.js:174391:30
BrowserRouter#http://localhost:3000/protis-react/static/js/vendors~main.chunk.js:174011:35
at me. I am not even sure where to track down what is missing a key, or where I missed putting it in. Any way for me to track it, or is someone here able to find the missing elements that need the unique keys. I tried to put a key on almost anything I generated originally, but I'm not sure what went wrong where, and this has been bothering me.
import React from 'react';
import {Menu, MenuButton, MenuDivider, MenuItem, SubMenu} from '#szhsin/react-menu';
import '#szhsin/react-menu/dist/index.css'
const tooltipStyle = {
position: "absolute",
pointerEvents: "none",
backgroundColor: "#D7D7A6",
border: "1px solid",
padding: "1px 8px",
whiteSpace: "nowrap",
zIndex: 200
};
class TopMenuDropdown extends React.Component {
state = {
pos: {x: 0, y: 0},
tooltip: '',
show: false
};
createTooltip = (tooltip) => ({
onMouseEnter: ({clientX, clientY}) => {
this.setState(
{
pos: {x: clientX, y: clientY},
tooltip: tooltip,
show: true
}
);
},
onMouseLeave: () => {
this.setState(
{
show: false
}
)
}
});
handleSelect = ({value}) => {
this.props.onClick(value);
}
renderMenuItem(menuAnchor, menuItems, isSubMenu) {
if (isSubMenu) {
return (
<SubMenu key={menuAnchor.id}
label={
<div
key={menuAnchor.id}
{...this.createTooltip(menuAnchor.attributes.title)}
>
{menuAnchor['#text']}
</div>
}
>
{this.menuGeneration(menuItems, menuAnchor)}
</SubMenu>
);
}
return (
<>
<Menu
style={{display: 'flex', float: 'left'}}
key={menuAnchor.id}
menuButton={
<MenuButton
key={menuAnchor.id}
{...this.createTooltip(menuAnchor.attributes.title)}>
{menuAnchor['#text']}
</MenuButton>}
onChange={({open}) => !open && this.setState({show: false})}
>
{this.menuGeneration(menuItems, menuAnchor)}
</Menu>
{this.state.show && (
<div
key={menuAnchor.id}
style={{
...tooltipStyle,
left: this.state.pos.x,
top: this.state.pos.y
}}
>
{this.state.tooltip}
</div>
)}
</>
);
}
menuGeneration(menuItems, menuAnchor) {
if (menuItems === undefined) {
return <></>;
}
if (!Array.isArray(menuItems)) {
menuItems = [menuItems];
}
return (
menuItems.map(({a, attributes, ul}) => {
if (ul !== undefined && ul.li !== undefined) {
return (
this.renderMenuItem(a, ul.li, true)
);
}
if (a === undefined) {
return (
<MenuDivider key={menuAnchor.id} />
)
}
return (
<MenuItem
key={menuAnchor.id}
value={a.attributes.id}
onClick={(id) => this.handleSelect(id)}
{...this.createTooltip(a.attributes.title)}
>
{a['#text']}
</MenuItem>)
}
)
)
}
render() {
if (!this.props.topMenu.hasOwnProperty('ul')) {
return null;
}
const menuItemRendering = this.props.topMenu.ul.li.map(({a, ul}) => {
return this.renderMenuItem(a, ul.li, false);
});
return (
<div style={{display: 'flex'}}>
{menuItemRendering}
</div>
)
}
}
export default TopMenuDropdown;
Issue is in renderMenuItem in the "else" branch when you render Menu and a div into a Fragment. When mapping JSX a React key needs to be on the outermost returned mapped element, the Fragment in this case.
renderMenuItem(menuAnchor, menuItems, isSubMenu) {
if (isSubMenu) {
return (
<SubMenu
...
>
{this.menuGeneration(menuItems, menuAnchor)}
</SubMenu>
);
}
return (
<Fragment key={menuAnchor.id}> // <-- add missing React key here
<Menu
...
>
{this.menuGeneration(menuItems, menuAnchor)}
</Menu>
{this.state.show && (
<div
...
>
{this.state.tooltip}
</div>
)}
</Fragment>
);
}
Try replacing key={menuAnchor.id} with key={a.id} for items directly in menuGeneration:
if (a === undefined) {
return <MenuDivider key={a.id} />
}
return (
<MenuItem
key={a.id}
value={a.attributes.id}
onClick={(id) => this.handleSelect(id)}
{...this.createTooltip(a.attributes.title)}
>
{a['#text']}
</MenuItem>
)
I would like to apply a class on a specific element of loop map.
const items = [
{
key: 'Menu1key',
content: 'Menu 1',
},
{
key: 'Menu2',
content: 'Menu2key'
},
{
key: 'Menu3Key',
content: 'Menu3',
children: [{
key: 'SubMenu3Key',
content: 'SubMenu3',
},
{
key: 'SubMenu5Key',
content: 'SubMenu5'
}]
},
{
key: 'Menu4Key',
content: 'Meu4',
children: [{
key: 'SubMenu4Key',
content: 'SubMenu4',
},
{
key: 'SubMenu5Key',
content: 'SubMenu5'
}]
}
]
const StackedMenu = (props) => {
let [chevronRotation, setChevronRotation] = useState('')
let [itemClicked, setItemClicked] = useState(props.itemClicked);
let [depth, setDepth] = useState(props.depth)
return (<Menu primary defaultActiveIndex={0} activeIndex={itemClicked} vertical pointing style={{ width: '100%' }}>
{props.items.map((item, index) => {
return (<>
<MenuItem onClick={(e, data) => { setItemClicked(data.index); setChevronRotation(`rotate`) }} index={index} key={index} pointing="start" >
<Menu.ItemContent style={depth > 0 ? { paddingLeft: '0.5em' } : null} >{item.content}</Menu.ItemContent>
{item.children ?
(<Menu.ItemIcon id={index} style={{ right: "0px", position: "absolute" }} className={props.itemClicked === index && item.children ? 'rotate' : ''}>
<ChevronEndMediumIcon />
</Menu.ItemIcon>) : null}
</MenuItem>
{itemClicked === index && item.children ? <div><StackedMenu items={item.children} depth={depth + 1} itemClicked={0} /></div> : null}
</>)
})}
</Menu>)
}
I using a recursive component to create children sub menu.
Actually when I open a menu all chevrons get expanded , what I'm looking for is that only the chevron of the clicked item get expanded.
Your problem is that you're setting a local state to control whether it should be open, but then checks on the props for the clicked item.
Change this line:
// I removed props from props.itemClicked
<Menu.ItemIcon id={index} style={{ right: "0px", position: "absolute" }} className={itemClicked === index && item.children ? 'rotate' : ''}>
You should use the component's state to check if menu item is opened or not.
Also, I've removed unnecessary depth state. Hope this could help.
const StackedMenu = ({items, depth, ...otherProps}) => {
let [chevronRotation, setChevronRotation] = useState('');
let [itemClicked, setItemClicked] = useState(otherProps.itemClicked);
return (
<Menu
primary
defaultActiveIndex={0}
activeIndex={itemClicked}
vertical
pointing
style={{width: '100%'}}>
{items.map((item, index) => {
return (
<>
<MenuItem
onClick={(e, data) => {
setItemClicked(data.index);
setChevronRotation(`rotate`);
}}
index={index}
key={index}
pointing="start">
<Menu.ItemContent
style={depth > 0 ? {paddingLeft: '0.5em'} : null}>
{item.content}
</Menu.ItemContent>
{item.children ? (
<Menu.ItemIcon
id={index}
style={{right: '0px', position: 'absolute'}}
className={itemClicked === index ? 'rotate' : ''}>
<ChevronEndMediumIcon />
</Menu.ItemIcon>
) : null}
</MenuItem>
{itemClicked === index && item.children ? (
<div>
<StackedMenu
items={item.children}
depth={depth + 1}
itemClicked={0}
/>
</div>
) : null}
</>
);
})}
</Menu>
);
};