I am having a page with multiple cards, where each card has a list of items.I have been trying out the implementation of Show More/ Show Less implementation on each of the card. For a single card, I am getting it right by adding two properties to the state object. But if I implement the same for all the cards, I end up adding more state object properties one for each card. Looking out for a better way, where I can use one common to handle all cards Show More/Less implementation
Just need an efficient approach to achieve this. Help would be appreciated
Example with only two cards
import * as React from 'react';
interface IState{
arr1:[],
arr2: [],
arr1ExpandFlag: boolean, // For updating Button label(More/Less)
arr2ExpandFlag : boolean
arr1temsToShow: number, // Number of items to show
arr2itemsToShow: number
//I will end up adding two variables one for each card
}
export default class App extends React.Component<{},IState> {
constructor(props:any){
super(props);
this.state = {
arr1 :[],
arr2 :[],
arr1ExpandFlag: false,
arr1ExpandFlag: false,
arr1itemsToShow: 3,
arr2itemsToShow: 3,
}
}
showMore = (type: string) => { // end up adding if conditions
if(type === 'arr1')
{
(this.state.arr1itemsToShow === 3) ?
(this.setState({ arr1itemsToShow: this.state.arr1.length, arr1ExpandFlag: true })):
(this.setState({ arr1itemsToShow: 3,arr1ExpandFlag: false}))
}
if(type === 'arr2')
{
(this.state.arr2itemsToShow === 3) ?
(this.setState({ arr2itemsToShow: this.state.arr2.length, arr2ExpandFlag: true })):
(this.setState({ arr2itemsToShow: 3, arr2ExpandFlag: false}))
}
}
render() // This is just for two cards
{
return
(
<div>
<div>
{this.state.arr1.slice(0, this.state.arr1itemsToShow).map((data : any, i) =>
<p key={i}>{data.user}</p>
)}
<a onClick={() => this.showMore('arr1')}>
{ (this.state.arr1ExpandFlag) ?
( <span>Show less</span>) :
( <span>Show more</span>)
}
</a>
</div>
<div>
{this.state.arr2.slice(0, this.state.arr2itemsToShow).map((data : any, i) =>
<p key={i}>{data.user}</p>
)}
<a onClick={() => this.showMore('arr2')}>
{ (this.state.arr2ExpandFlag) ?
(<span>Show less</span>) :
(<span>Show more</span>)
}
</a>
</div>
</div>
)
}
As others have said - build it out into reusable components. Start by identifying places where you're repeating yourself and extracting those out. It becomes easier with practice.
I would start with Card. You can use a card type (you could also use an interface but this is personal preference) and then add an onClickExpand handler to form the Props for this control.
type Item = {
user: string;
}
type Card = {
items: Item[];
expanded?: boolean;
numItemsShowing: number;
}
type CardProps = Card & {
onClickExpand: () => void;
};
const Card = (props: CardProps) => {
const { expanded, items, numItemsShowing, onClickExpand } = props;
return (
<div>
{items.slice(0, numItemsShowing).map((data, i) => (
<p key={i}>{data.user}</p>
))}
<a onClick={onClickExpand}>
{expanded
? <span>Show less</span>
: <span>Show more</span>
}
</a>
</div>
);
}
Then your app can be simpler:
type State = {
card: Card[];
}
export default class App extends React.Component<{}, State> {
state = { cards: [] };
showMore = (index: number) => () => {
this.setState({
cards: this.state.cards.map((card, i) => {
// Only modify card if it's the index we care about
if (i === index) {
return {
...card,
expanded: true,
}
}
return card;
})
});
}
addCard = () => {
const newCard = {
items: [],
expanded: false,
numItemsShowing: 0
};
this.setState({
cards: [ ...this.state.cards, newCard ] // add to end of array
});
}
render() {
return (
<div>
{this.state.cards.map((card, i) =>
<Card
{...card}
onClickExpand={this.showMore(i)}
key={i}
/>
)}
<button onClick={this.addCard}>Add Card</button>
</div>
)
}
}
This app lets you click the button to add a new card into the state.