binding multiple ul li subcategeries under categeries - reactjs

I have some categories list, I have to develop dynamic dropdown based on user click I have to call API and need to bind subcategories under categories.
I have to bind subcategories for particular li but it binding for every category li.
till now I have tried this,
subCategeries = ({subCategeries, index}) => {
return (
<ul>
{subCategeries.map(subCategeries => {
return (<li key={subCategeries.id} onClick={(event) => {
this.getSubSubCategeries(event, subCategeries.id)
}}>
{subCategeries.name}
<this.subSubCategeries subSubCategeries={this.state.subSubCategeries}/>
</li>);
})}
</ul>
);
};
subSubCategeries = ({subSubCategeries}) => {
return (
<ul>
{subSubCategeries.map(subSubCategeries => {
return (<li key={subSubCategeries.id}>
{subSubCategeries.name}
</li>);
})}
</ul>
);
};
render() {
var self = this;
console.log(self);
var mainCategeries = this.state.mainCategeries && this.state.mainCategeries.length > 0 && this.state.mainCategeries.map((obj, index) =>
<li key={index}><a onClick={(event) => {
this.getSubCategeries(event, obj.id)
}}>{obj.name}
{this.state.subCategeries.length > 0 ? (
<this.subCategeries index={index} subCategeries={this.state.subCategeries}/>)
: ''
}
</a>
</li>);
return (
<ul>{mainCategeries}
</ul>
);
}

Related

toggle is not toggling element but it's collapsing element in navigation

I'm creating navigation for mobile devices. Everything works, but when I'm trying to open third level of navigation, it doesn't open, but collapses previous level. I dont know what to do and I'm pretty dissapointed, because it seems like easy thing and I feel like dumb. Someone have any tips please? :(
function MobileCategoriesLinks(props: MobileLinksProps) {
const { data } = useCollectionsQuery();
const { level = 0, onItemClick } = props;
const handleItemClick = (item: any) => {
if (onItemClick) {
onItemClick(item);
}
};
const linksList = data?.collections.items.map((department, index) => {
let item;
if(department.parent?.id === "1") {
const renderItem: RenderItemFn = ({ toggle, setItemRef, setContentRef }) => {
let arrow = null;
let subLinks = null;
let linkOrButton = null;
let link = "/" + department.children
if(department.children?.length) {
arrow = (
<button className="mobile-links__item-toggle" type="button" onClick={toggle}>
<ArrowRoundedDown12x7Svg className="mobile-links__item-arrow" />
</button>
);
}
let depChild = department.children?.map((name) => {
let x = name.name
let link = "/" + name.slug
let child = name
let c = child.children
if(child.children?.length) {
let thirdChild = child.children?.map((item, index) =>{
return item.name
})
let thirdChildSlug = child.children?.map((item, index) =>{
return item.slug
})
arrow = (
<button className="mobile-links__item-toggle" type="button" onClick={toggle}>
<ArrowRoundedDown12x7Svg className="mobile-links__item-arrow" />
</button>
);
subLinks = (
<div className="mobile-links__item-sub-links" ref={setContentRef}>
<ul className="mobile-links mobile--links--level--3">
<li>
<div className="mobile-links__item">
<div className="mobile-links__item-title">
{thirdChild}
</div>
</div>
</li>
</ul>
</div>
)
if(child.children?.length) {
linkOrButton = (
<AppLink
href={"/" + child.slug}
className="mobile-links__item-link"
onClick={() => handleItemClick(link)}
>
{child.name}
</AppLink>
)}
return (
<li>
<div className="mobile-links__item" ref={setItemRef}>
<div className="mobile-links__item-title">
<AppLink
href={"/" + child.slug}
className="mobile-links__item-link"
onClick={() => handleItemClick(link)}
>
{name.name}
</AppLink>
{arrow}
</div>
{subLinks}
</div>
</li>
)}
return (
<li>
<div className="mobile-links__item">
<div className="mobile-links__item-title">
{x}
</div>
</div>
</li>
);
}
)
subLinks = (
<div className="mobile-links__item-sub-links" ref={setContentRef}>
<ul className="mobile-links mobile--links--level--2">
{depChild}
</ul>
</div>
)
if(department.children) {
linkOrButton = (
<AppLink
href={"/" + department.slug}
className="mobile-links__item-link"
onClick={() => handleItemClick(link)}
>
{department.name}
</AppLink>
)} else {
linkOrButton = (
<button
type="button"
className="mobile-links__item-link"
onClick={() => handleItemClick(link)}
>
{department.name}
</button>
)
}
return (
<div className="mobile-links__item" ref={setItemRef}>
<div className="mobile-links__item-title">
{linkOrButton}
{arrow}
</div>
{subLinks}
</div>
);
};
item = <Collapse toggleClass="mobile-links__item--open" render={renderItem} />;
}
return <li key={index}>{item}</li>
}
);
return ( <ul className={`mobile-links mobile-links--level--1`}>
{linksList}
</ul>
);
}
export default withApollo(MobileCategoriesLinks)

React child not re-rendered when parents props change

I'm having some issues with child re-rendering, I pass methods to children to see if a button should be displayed or not but when the state of the parent changes, the children are not re-rendered.
I tried with the disabled attribute for the button but didn't work either.
Here's my code (I removed unnecessary part):
function Cards(props) {
const isCardInDeck = (translationKey) => {
return props.deck.some(
(card) => !!card && card.translationKey === translationKey
);
};
const addToDeck = (card) => {
if (!isCardInDeck(card.translationKey) && !!card) {
props.deck.push(card);
}
};
const removeFromDeck = (card) => {
if (isCardInDeck(card.translationKey) && !!card) {
var index = props.deck.findIndex(
(c) => c.translationKey === card.translationKey
);
props.deck.splice(index, 1);
}
};
return (
<div className="cardsContent">
<div className="cards">
{cardList.length > 0 ? (
cardList.map((item, index) => {
return (
<Card key={index} card={item} addToDeckDisabled={isCardInDeck(item.translationKey)} addToDeckClick={addToDeck} removeFromDeckClick={removeFromDeck} />
);
})
) : (
<span>
<FormattedMessage id="app.cards.label.no.card.found" defaultMessage="No card found with filter."/>
</span>
)}
</div>
</div>
);
}
function Card(props) {
const toggleShowDescription = () => {
if (!showDescription) {
setShowDescription(!showDescription);
}
};
return (
<div onClick={toggleShowDescription} onBlur={toggleShowDescription} >
<img src={"../images/cards/" + props.card.image} alt={props.card.image + " not found"} />
{showDescription ? (
<div className="customCardDetail">
<div className="cardName"></div>
<div className="cardType">
{props.addToDeckDisabled ? (
<Button onClick={() => { props.removeFromDeckClick(props.card);}} startIcon={<RemoveIcon />}>
Remove from deck
</Button>
) : (
<Button onClick={() => { props.addToDeckClick(props.card); }} startIcon={<AddIcon />}>
Add to deck
</Button>
)}
</div>
<div className="cardDescription">
<span>
<FormattedMessage id={props.card.description} defaultMessage={props.card.description} />
</span>
</div>
</div>
) : (
""
)}
</div>
);
}
You code does not update state. Cards mutates the props that it is receiving.
To use state in a functional component in React you should use the useState hook.
Cards would then look something like this:
function Cards(props) {
const [deck, setDeck] = useState(props.initialDeck)
const isCardInDeck = (translationKey) => {
return deck.some(
(card) => !!card && card.translationKey === translationKey
);
};
const addToDeck = (card) => {
if (!isCardInDeck(card.translationKey) && !!card) {
setDeck([...deck, card])
}
};
const removeFromDeck = (card) => {
if (isCardInDeck(card.translationKey) && !!card) {
setDeck(deck.filter(deckItem => deckItem.translationKey !== card.translationKey))
}
};
return (
<div className="cardsContent">
<div className="cards">
{cardList.length > 0 ? (
cardList.map((item, index) => {
return (
<Card key={index} card={item} addToDeckDisabled={isCardInDeck(item.translationKey)} addToDeckClick={addToDeck} removeFromDeckClick={removeFromDeck} />
);
})
) : (
<span>
<FormattedMessage id="app.cards.label.no.card.found" defaultMessage="No card found with filter."/>
</span>
)}
</div>
</div>
);
}

can't use the Refs properly in react js, Cannot read property of undefined

I am very new to react and js,
I have a menu and submenu, I use a list to map data,
I want to write a function, so onmouseover one item in the list,
if it has submenu, it will show.
the problem is that I can't select the submenu using ref.
It is just too complicated for me, any help would be much appreciated!
enter image description here
import React, { Component } from "react";
export class Menu extends Component {
constructor(props) {
super(props);
this.liRefs = [];
}
showSubmenu = (e) => {
// this.liRefs.current.style.display = "block";
for (var i = 0; i < this.liRefs.length; i++) {
this.liRefs[i].current.style.display = "block";
}
// console.log(this.liRefs[10]);
};
getStyle = (e) => {
e.target.style.background = "red";
};
render() {
return (
<ul className="mainmenu">
{this.props.items.map((i) =>
i.subitems ? (
<li key={i.id} onMouseOver={this.showSubmenu}>
{i.icon}
{i.name}
<ul key={i.id} ref={(ref) => (this.liRefs[i.id] = ref)}>
{i.subitems.map((item) => (
<div key={item.id} className="submenu">
{item.icon}
{item.name}
</div>
))}
</ul>
</li>
) : (
<li key={i.id}>
{i.icon}
{i.name}
{i.img}
</li>
)
)}
</ul>
);
}
}
export default Menu;
You are giving ref value to this. liRefs[i.id] and accessing through this. liRefs[i] so that both are the different change your code as below:
{this.props.items.map((i,index) =>
i.subitems ? (
<li key={i.id} onMouseOver={this.showSubmenu}>
{i.icon}
{i.name}
<ul key={i.id} ref={(ref) => (this.liRefs[i.id] = ref)}>
{i.subitems.map((item) => (
<div key={item.id} className="submenu">
{item.icon}
{item.name}
</div>
))}
</ul>
</li>
) : (
<li key={i.id}>
{i.icon}
{i.name}
{i.img}
</li>
)
)}

How do you toggle filtered list items inside of a div in React?

I'm building a React component that shows a filtered list of items in a div when users click on a button. Only the items within that div should be displayed on click. For some reason, though, the lists for every section are being toggled.
What am I doing wrong?
Here is my code: https://codesandbox.io/s/6yr0jzlpwn
Simply you can just define a specific value for each button then pass it to state
<div>
<h1>{this.state.title}</h1>
<div>
<button value={'1'} onClick={this.toggleWords}>肉</button>
{this.state.showWords === '1' && (
<ul>
{this.state.list.filter(function(word) {
return word[1] === "肉";
}).map(function (word) {
return <li>{word}</li>;
})}
</ul>
)}
</div>
<div>
<button value={'2'} onClick={this.toggleWords}>茶</button>
{this.state.showWords === '2' && (
<ul>
{this.state.list.filter(function(word) {
return word[1] === "茶";
}).map(function(word) {
return <li>{word}</li>;
})}
</ul>
)}
</div>
<div>
<button value={'3'} onClick={this.toggleWords}>日</button>
{this.state.showWords === '3' && (
<ul>
{this.state.list.filter(function(word) {
return word[0] === "日";
}).map(function(word) {
return <li>{word}</li>;
})}
</ul>
)}
</div>
</div>
In toggleWords function
toggleWords(e) {
const clickedButton = e.target.value;
if(clickedButton !== this.state.showWords){
this.setState({ showWords: clickedButton })
}else{
this.setState({ showWords: '' }) // handle close list if double click
}
}
In case if you want to expand two sections at once you need to change showWords state to be an array then use indexOf method to extend the section
<div>
<h1>{this.state.title}</h1>
<div>
<button value={'1'} onClick={this.toggleWords}>肉</button>
{this.state.showWords.indexOf('1') !== -1 && (
<ul>
{this.state.list.filter(function (word) {
return word[1] === "肉";
}).map(function (word) {
return <li>{word}</li>;
})}
</ul>
)}
</div>
<div>
<button value={'2'} onClick={this.toggleWords}>茶</button>
{this.state.showWords.indexOf('2') !== -1 && (
<ul>
{this.state.list.filter(function (word) {
return word[1] === "茶";
}).map(function (word) {
return <li>{word}</li>;
})}
</ul>
)}
</div>
<div>
<button value={'3'} onClick={this.toggleWords}>日</button>
{this.state.showWords.indexOf('3') !== -1 && (
<ul>
{this.state.list.filter(function (word) {
return word[0] === "日";
}).map(function (word) {
return <li>{word}</li>;
})}
</ul>
)}
</div>
</div>
Then in toggleWords function will delete the value from array if exist else it will add it
toggleWords(e) {
const clickedButton = e.target.value;
if (this.state.showWords.indexOf(clickedButton) !== -1) { // deleting the value from array if exist
this.setState(prevState => ({ showWords: this.state.showWords.filter(d => d !== clickedButton) }))
} else {
this.setState(prevState => ({ showWords: [...prevState.showWords, clickedButton] }))
}
}

Get Key Index on Click ES6 React

I have the following component
const list = (props) => {
const handler = function(){
};
var listItems = props.items.map(function(item, index){
return (
<li key={index} onClick={ handler }>
{item.text}
</li>
)
});
return (
<div>
<ul>
{listItems}
</ul>
</div>
)
}
On Click i'd like to get the index of the li clicked. Using ES6 and without binding how can i do this ?
Use an arrow function.
onClick={() => handler(index)}
You can actually get index without using an arrow function. The only thing you need to do is pass the index as an attribute and get that value from the event as e.target.getAttribute("your_attribute_name")
const list = (props) => {
const handler = function(e){
console.log(e.target.getAttribute("data-index")); //will log the index of the clicked item
};
var listItems = props.items.map(function(item, index){
return (
<li key={index} data-index={index} onClick={ handler }>
{item.text}
</li>
)
});
return (
<div>
<ul>
{listItems}
</ul>
</div>
);
}
you can set the index in the child as data-index and then you get this value in the handler function using event.currentTarget.dataset.index
This will prevent the re-rendering that causes when you use arrow function within render.
const handler = (event) => {
console.log(event.currentTarget.dataset.index);
};
const listItems = props.items.map((item, index) => {
return (
<li key={index} data-index={index} onClick={handler}>
{item.text}
</li>
)
});
This also works:
const list = props => {
const handler = index => () => {
}
const listItems = props.items.map((item, index) =>
<li key={index} onClick={handler(index)}>
{item.text}
</li>)
return <div>
<ul>{listItems}</ul>
</div>
}
You have another way of doing it, really easy to pass any variable to the handleClick function.
An that is using a curry function.
const ListComponent= ({listItems}) => {
const handleClick = (index) => (event) => {
[...]
}
return (
<ul>
{listItems.map((item, index) => (
<li
key={index}
onClick={ handler(index) }
>
{item.text}
</li>
))}
</ul>
)
}

Resources