user7496931
user7496931

Reputation: 1519

Toggling a classname for one button in a list of buttons

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

Answers (1)

Cat_Enthusiast
Cat_Enthusiast

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

Tags.js

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.

Parent Component

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

Related Questions