简体   繁体   中英

React toggle button after mapping through list

After getting results from api call to Google books i'd like to hide the description paragraphs and have a toggle button using the css class of hidden (tailwinds css). I'm currently just console.logging the elements on the "view description" button & I'm just not sure how to target a single element after looping through the nodeList with my toggleDesc() function

React SearchBar component

import React, { useState, useEffect } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import axios from 'axios';

import { faSearch } from '@fortawesome/free-solid-svg-icons';

import SearchResult from '../search-result/search-result.component';

const SearchBar = () => {
  const [searchTerm, setSearchTerm] = useState('');
  const [books, setBooks] = useState({ items: [] });
  
  useEffect(() => {
      async function fetchBooks() {
          const newRes = await fetch(`${API_URL}?q=${searchTerm}`);
          const json = await newRes.json();
          const setVis = Object.keys(json).map(item => ({
              ...item, isDescVisible: 'false'
          }))
          setBooks(setVis);
      }
      fetchBooks();
  }, []);

  const toggleDesc = (id) => {
    const newBooks = books.items.map(book => book.id === id ? {...book, isDescVisible: !book.isDescVisible} : book);
    console.log(newBooks);
    setBooks(newBooks);
}

  const onInputChange = (e) => {
    setSearchTerm(e.target.value);
  };

  let API_URL = `https://www.googleapis.com/books/v1/volumes`;

  const fetchBooks = async () => {
    // Ajax call to API via axios
    const result = await axios.get(`${API_URL}?q=${searchTerm}`);

    setBooks(result.data);
    
  };

  // Handle submit
  const onSubmitHandler = (e) => {
    // prevent browser from refreshing
    e.preventDefault();
    // call fetch books async function
    fetchBooks();
  };

  // Handle enter press
  const handleKeyPress = (e) => {
      if (e.key === 'Enter') {
          e.preventDefault();

          fetchBooks();

      }
  }


  return (
    <div className="search-bar p-8">
      <div className="bg-white flex items-center rounded-full shadow-xl">
        <input
          className="rounded-l-full w-full py-4 px-6 text-gray-700 leading-tight focus:outline-none"
          id="search"
          type="text"
          placeholder="Try 'The Hunt For Red October by Tom Clancy' "
          onChange={onInputChange}
          value={searchTerm}
          onKeyPress={handleKeyPress}
        />
        <div className="p-4">
          <button
            onClick={onSubmitHandler}
            className="bg-blue-800 text-white rounded-full p-2 hover:bg-blue-600 focus:outline-none w-12 h-12 flex items-center justify-center"
          >
            <FontAwesomeIcon icon={faSearch} />
          </button>
        </div>
      </div>
      <div className='result mt-8'>
      <ul>
         <SearchResult books={books} toggleDesc={toggleDesc} />
      </ul>
      </div>
    </div>
  );
};

export default SearchBar;

SearchResults Component

import React from 'react';
import { LazyLoadImage } from 'react-lazy-load-image-component';
import 'react-lazy-load-image-component/src/effects/blur.css';

import './search-result.styles.scss';

const SearchResult = ({ books, toggleDesc }) => {

  return (
    <div className="search-result mb-6">
      {books.items !== undefined &&
        books.items !== null &&
        books.items.map((book, index) => {
          return (
            <div key={index} className="book-info mb-2">
              <li className="ml-4">
                <div className="flex">
                  <LazyLoadImage
                    className="book-img px-4 py-2"
                    effect="blur"
                    alt={`${book.volumeInfo.title} book`}
                    src={`http://books.google.com/books/content?id=${book.id}&printsec=frontcover&img=1&zoom=1&source=gbs_api`}
                  />
                  <div className="flex-1">
                    <h3 className="text-2xl">{book.volumeInfo.title}</h3>
                    <div>
                      <p className="flex">
                        <button
                          onClick={() => toggleDesc(book.id)}
                          className="bg-blue-800 mt-2 text-gray-200 rounded hover:bg-blue-400 px-4 py-3 text-sm focus:outline-none"
                          type="button"
                        >
                          View Description
                        </button>
                      </p>
                      {book.isDescVisible &&
                           <div
                           className="block border px-4 py-3 my-2 text-gray-700 desc-content"
                         >
                           <p>{book.volumeInfo.description}</p>
                         </div>
                      }
                    </div>
                    <h3 className="text-xl text-blue-800 mt-2 p-2">
                      Average time to read:{' '}
                    </h3>
                  </div>
                </div>
                <hr />
              </li>
            </div>
          );
        })}
    </div>
  );
};

export default SearchResult;

console

在此处输入图像描述

You will have to add a property to each item of your books to handle the description visibility and change it when you click the button, this is a basic example

useEffect(()=> {
  fetch(url).then(res => {
    const newRes = res.map(item=> ({ ...item, isDescVisible: 'false' })) // <— add the new property to all items set to false
    setBooks(newRes);
  })
})
<p className='flex'>
  <button
     onClick={() => toggleDesc(book.id)} // <—- pass the id or the whole book
     className='bg-blue-800 mt-2 text-gray-200 rounded hover:bg-blue-400 px-4 py-3 text-sm focus:outline-none'
     type='button'
  >
    View Description
  </button>
</p>

//here show the desc according to the value of the property you added, no need for the hidden class
{book.isDescVisible && <div className='block border px-4 py-3 my-2 text-gray-700 desc-content'>
  <p>{book.volumeInfo.description}</p>
</div>}

This function needs to be on the parent where you are setting the state

const toggleDesc = (id) => {
  const newBooks = books.map(book => book.id === id ? {...book, isDescVisible: !book.isDescVisible} : book); <-- toggle the property
  setBooks(newBooks); <—- save it in the state again
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM