Reputation: 1519
I have a list of buttons and I'm trying to toggle the classname when one is clicked. So that only when I click on a specific button is highlighted. I have a TagList
component that looks like this:
const Tags = ({tags, onTagClick}) => {
return (
<div className="tags-container">
{ tags.map(tag => {
return (
<span
key={tag.name}
className="tag"
onClick={() => onTagClick(tag)}
>
{tag.name} | {tag.numberOfCourses}
</span>
)
})
}
</div>
)
}
And this is found in the parent component:
onTagClick = (tag) => {
this.filterCourses(tag)
}
render() {
const { tags, courses } = this.state
return (
<div>
<h1> Course Catalog Component</h1>
<Tags tags={tags} onTagClick={this.onTagClick} />
<Courses courses={courses} />
</div>
)
}
I know how I could toggle the class for a single button but I'm a little confused when it comes to a list of buttons. How can I toggle one specifically from a list of buttons? Am I going to need a seperate Tag
component and add state to that one component?
EDIT:
This is what my state currently looks like:
constructor(props) {
super(props)
this.state = {
tags: this.sortedTags(),
courses: courses
}
}
And this is what filterCourses
looks like:
filterCourses = (tag) => {
this.setState({
courses: courses.filter(course => course.tags.includes(tag.name))
})
}
Upvotes: 0
Views: 100
Reputation: 15688
To start, you would want to give each tag object you're working with a selected
property. That will make it easier for you to toggle the class. During the rendering of that markup.
Here is the working sandbox: https://codesandbox.io/s/stupefied-cartwright-6zpxk
import React from "react";
const Tags = ({ tags, onTagClick }) => {
return (
<div className="tags-container">
{tags.map(tag => {
return (
<div
key={tag.name}
className={tag.selected ? "tag selected" : "tag"}
onClick={() => onTagClick(tag)}
>
{tag.name} | {tag.numberOfCourses}
</div>
);
})}
</div>
);
};
export default Tags;
Then in the Parent component, we simply toggle the selected prop (True/False) when the tag is clicked. That will update the tags-array and it gets passed back down to the Child-component which now has the new selected values.
import React from "react";
import ReactDOM from "react-dom";
import Tags from "./Tags";
import Courses from "./Courses";
import "./styles.css";
class App extends React.Component {
state = {
tags: [
{ id: 1, name: "math", numberOfCourses: 2, selected: false },
{ id: 2, name: "english", numberOfCourses: 2, selected: false },
{ id: 3, name: "engineering", numberOfCourses: 2, selected: false }
],
courses: [
{ name: "Math1a", tag: "math" },
{ name: "Math2a", tag: "math" },
{ name: "English100", tag: "english" },
{ name: "English200", tag: "english" },
{ name: "Engineering101", tag: "engineering" }
],
sortedCourses: []
};
onTagClick = tag => {
const tagsClone = JSON.parse(JSON.stringify(this.state.tags));
let foundIndex = tagsClone.findIndex(tagClone => tagClone.id == tag.id);
tagsClone[foundIndex].selected = !tagsClone[foundIndex].selected;
this.setState(
{
tags: tagsClone
},
() => this.filterCourses()
);
};
filterCourses = () => {
const { tags, courses } = this.state;
const selectedTags = tags.filter(tag => tag.selected).map(tag => tag.name);
const resortedCourses = courses.filter(course => {
return selectedTags.includes(course.tag);
});
this.setState({
sortedCourses: resortedCourses
});
};
render() {
const { tags, sortedCourses, courses } = this.state;
return (
<div>
<h1> Course Catalog Component</h1>
<Tags tags={tags} onTagClick={this.onTagClick} />
<Courses courses={!sortedCourses.length ? courses : sortedCourses} />
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Upvotes: 1