Ade N
Ade N

Reputation: 145

React - hide and show individual element

I am trying to toggle show/hide on individual elements. I thought the key={item._id} will be enough to toggle the individual items, but when clicking the toggle button every element is revealed.

This is my attempt: https://codesandbox.io/s/thirsty-torvalds-xnkt1?file=/src/App.js

I would really appreciate any suggestion. Thanks

Upvotes: 1

Views: 1486

Answers (5)

RIYAJ KHAN
RIYAJ KHAN

Reputation: 15292

Updated and working code here

Changes in code:

In list data structure

const list = [
  {
    _id: "1",
    book: "Witcher 1",
    author: "Andrzej Sapkowski",
    show: false // add additional key to toggel
  },
  ...
];

In constructor

constructor(props) {
    super(props);
    this.state = {
      books: list // defined books state
    };
  }

in handleShow

handleShow = event => {
    console.log(event.target.id);
    const books = this.state.books;
    const bookId = event.target.id; // get selected book id

    for (let index = 0; index < books.length; index++) {
      if (bookId === books[index]._id) {
        books[index].show = !books[index].show; //toggle the element 
      }
    }
    this.setState({ books });
  };

In render:

render() {
const listItem = this.state.books.map(item => { //use state to iterate
  return (
    <div key={item._id} className="flex p-4">
      <div key={item._id}>{item.book}</div>
      <button
        id={item._id} //add key to get the selected book
        onClick={this.handleShow}
        className="px-4 font-bold bg-gray-300"
      >
        toggle
      </button>
      <div>{item.show ? <div>{item.author}</div> : null}</div> // use key  `show` to toggle it 
    </div>
  );
});

Upvotes: 1

Jeeva
Jeeva

Reputation: 1590

You can split that in to a component and maintain the toggle state there which is "show". I have updated my answer in your CodeSandbox link click here

//App.js

import React from "react";
import "./styles.css";
import "./styles/tailwind-pre-build.css";
import Book from './Book';

const list = [
  {
    _id: "1",
    book: "Witcher 1",
    author: "Andrzej Sapkowski"
  },
  {
    _id: "2",
    book: "Witcher 2",
    author: "Andrzej "
  },
  {
    _id: "3",
    book: "Witcher 3",
    author: "Andrzej Sapkowski"
  }
];

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {

    };
  }

  render() {
    const listItem = list.map(item => {
      return (
        <div className="flex p-4">
          <div key={item._id}>{item.book}</div>
          {/* <button
            onClick={this.handleShow}
            className="px-4 font-bold bg-gray-300"
          >
            toggle
          </button>
          <div>{this.state.show ? <div>{item.author}</div> : null}</div> */}
          <Book item={item}/>
        </div>
      );
    });

    return (
      <div className="text-2xl">
        <div>List of books</div>
        {listItem}
      </div>
    );
  }
}

// Book.js
import React from "react";
import "./styles.css";
import "./styles/tailwind-pre-build.css";

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      show: false
    };
  }

  handleShow = () => {
    this.setState({
      show: !this.state.show
    });
  };

  render(){
    return(
      <>
          <button
            onClick={this.handleShow}
            className="px-4 font-bold bg-gray-300"
          >
            toggle
          </button>
          <div>{this.state.show ? <div>{this.props.item.author}</div> : null}</div>
      </>
    )    
  }
}

Upvotes: 3

chonnychu
chonnychu

Reputation: 1116

you can save list into state and add a isToggle prop for every object, and handleShow change the isToggle state of clicked target item, code will look like this:

import React from "react";
import "./styles.css";
import "./styles/tailwind-pre-build.css";

const list = [
  {
    _id: "1",
    book: "Witcher 1",
    author: "Andrzej Sapkowski",
    isToggle: false,
  },
  {
    _id: "2",
    book: "Witcher 2",
    author: "Andrzej ",
    isToggle: false,
  },
  {
    _id: "3",
    book: "Witcher 3",
    author: "Andrzej Sapkowski",
    isToggle: false,
  }
];

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      list
    };
  }

  handleShow = (e) => {
    const index = this.state.list.findIndex(t => t._id === e.currentTarget.dataset.id);
    const list = [...this.state.list];
    list[index].isToggle = !list[index].isToggle;
    this.setState({ list });
  };

  render() {
    const listItem = list.map(item => {
      return (
        <div className="flex p-4">
          <div key={item._id}>{item.book}</div>
          <button
            data-id={item._id}
            onClick={this.handleShow}
            className="px-4 font-bold bg-gray-300"
          >
            toggle
          </button>
          <div>{item.isToggle ? <div>{item.author}</div> : null}</div>
        </div>
      );
    });

    return (
      <div className="text-2xl">
        <div>List of books</div>
        {listItem}
      </div>
    );
  }
}

Upvotes: 2

Red Baron
Red Baron

Reputation: 7642

if you want to show them toggled individually you can do like this:

import React from "react";
import "./styles.css";
import "./styles/tailwind-pre-build.css";

const list = [
  {
    _id: "1",
    book: "Witcher 1",
    author: "Andrzej Sapkowski"
  },
  {
    _id: "2",
    book: "Witcher 2",
    author: "Andrzej "
  },
  {
    _id: "3",
    book: "Witcher 3",
    author: "Andrzej Sapkowski"
  }
];

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      items: false,
      showId: null,
    };
  }

  handleShow = (id) => {
    this.setState({
      showId: id
    });
  };

  render() {
    const listItem = list.map(item => {
      return (
        <div className="flex p-4">
          <div key={item._id}>{item.book}</div>
          <button
            onClick={() => this.handleShow(item._id)}
            className="px-4 font-bold bg-gray-300"
          >
            toggle
          </button>
          <div>{this.state.showId === item._id ? <div>{item.author}</div> : null}</div>
        </div>
      );
    });

    return (
      <div className="text-2xl">
        <div>List of books</div>
        {listItem}
      </div>
    );
  }
}

Upvotes: 1

You can just change the show variable in the state to take the index of the element like this :

 handleShow = (idx) => {
    this.setState({
      show: idx
    });
  };

and your constructor becomes :

 constructor(props) {
    super(props);
    this.state = {
      show: -1
    };
  }

And your list becomes :

 const listItem = list.map(item => {
      return (
        <div className="flex p-4">
          <div key={item._id}>{item.book}</div>
          <button
            onClick={() => this.handleShow(item._id)}
            className="px-4 font-bold bg-gray-300"
          >
            toggle
          </button>
          <div>{this.state.show === item._id ? <div>{item.author}</div> : null}</div>
        </div>
      );
    });

I think that will do it !

Upvotes: 2

Related Questions