Reputation:
With a React Accordion I wanted to create a function that will only show the information of the belonging episode. But now it seems that all blocks are doing the same thing. How can I build that function so one block will be toggling instead of all?
export const HomePage: React.FC<IProps> = () => {
const [open, setOpen] = useState(false);
return (
<div>{data.characters.results.map((character: { id: number, name: string, image: string; episode: any; }, index: number) => (
<div key={character.id}>
<CharacterHeading>{character.name}</CharacterHeading>
<CharacterImage src={character.image} alt={character.name} />
{character.episode.map((char: { name: string; air_date: string; episode: string; characters: any, id: number; }) => (
<div key={char.id}>
{char.name}
{char.air_date}
{char.episode}
<AccordionButton open={open}
onClick={() => setOpen(!open)}>
Check all characters
</AccordionButton>
<EpisodeListSection open={open}>
{char.characters.map((ep: { id: number, name: string; image: string; }, index: number) => (
<EpisodeInfo key={ep.id}>
<EpisodeInfoBlock>
<EpisodeName>{ep.name}</EpisodeName>
<EpisodeImage src={ep.image} alt={ep.name} />
</EpisodeInfoBlock>
</EpisodeInfo>
))}
</EpisodeListSection>
</div>
))}
</div>
))
}</div>
);
};
Upvotes: 1
Views: 254
Reputation: 1133
Here is the updated code that you can use:
import { useState } from "react";
import { gql, useQuery } from "@apollo/client";
import {
AccordionButton,
CharacterImage,
CharacterHeading,
EpisodeInfo,
EpisodeImage,
EpisodeInfoBlock,
EpisodeListSection,
EpisodeName
} from "./HomePage.styles";
export const GET_CHARACTER = gql`
query {
characters(page: 2, filter: { name: "rick" }) {
results {
name
image
gender
episode {
id
name
air_date
episode
characters {
id
name
image
}
}
}
}
location(id: 1) {
id
}
episodesByIds(ids: [1, 2]) {
id
}
}
`;
export interface Episodes {
name: string;
air_data: string;
episode: string;
characters: Array<any>;
}
export interface IProps {
episodeList?: Episodes[];
}
export const HomePage: React.FC<IProps> = () => {
const { data, loading, error } = useQuery(GET_CHARACTER);
const [inputValue, setInputValue] = useState("");
const [open, setOpen] = useState(false);
const [selectedId, setSelectedId] = useState(0);
const onChangeHandler = (text: string) => {
setInputValue(text);
};
const onSelectItem = (selectedItemId: number) => {
if (selectedId !== selectedItemId) {
setSelectedId(selectedItemId);
} else {
setSelectedId(-1);
}
};
if (loading) return <p>loading</p>;
if (error) return <p>ERROR: {error.message}</p>;
if (!data) return <p>Not found</p>;
return (
<div>
<input
type="text"
name="name"
onChange={(event) => onChangeHandler(event.target.value)}
value={inputValue}
/>
<div>
{data.characters.results.map(
(
character: {
id: number;
name: string;
image: string;
episode: any;
},
index: number
) => (
<div key={character.id}>
<CharacterHeading>{character.name}</CharacterHeading>
<CharacterImage src={character.image} alt={character.name} />
{character.episode.map(
(char: {
name: string;
air_date: string;
episode: string;
characters: any;
id: number;
}) => (
<div key={char.id}>
{char.name}
{char.air_date}
{char.episode}
<AccordionButton
onClick={() => onSelectItem(char.id)}
open={char.id === selectedId ? true : false}
>
Check all characters
</AccordionButton>
<EpisodeListSection
open={char.id === selectedId ? false : true}
>
{char.characters.map(
(
ep: { id: number; name: string; image: string },
index: number
) => (
<EpisodeInfo key={ep.id}>
<EpisodeInfoBlock>
<EpisodeName>{ep.name}</EpisodeName>
<EpisodeImage src={ep.image} alt={ep.name} />
</EpisodeInfoBlock>
</EpisodeInfo>
)
)}
</EpisodeListSection>
</div>
)
)}
</div>
)
)}
</div>
</div>
);
};
Upvotes: 0
Reputation: 2102
You only have one open
variable that you are passing to every accordion. Thus, when one accordion changes that value with setOpen
, it changes for all of them.
If AccordionButton is a component you built, I would suggest letting that component handle its own open/closed state. Your parent doesn't seem like it needs to control that. Otherwise, you will need open
to be an array of values, you will need to only pass the correct open
value to each accordion, and you will need to have your onClick
function tell the parent which open
value it needs to change.
Again, that seems like a lot of work for not a lot of utility. Better to just let the accordions keep track of whether they're open.
Upvotes: 1