Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
3.6k views
in Technique[技术] by (71.8m points)

reactjs - React: How to update state for just one element, rather than batch update

I am a beginner with React. I have a project I'm working on with some sample travel tours. I would like to use a "read more/show less" feature for the description of each tour. The read more/show less button is toggling, but it's showing more or less description for all of the tours when clicked, when I want it to just toggle the tour that's clicked. In other words, it's updating the state for ALL tours, rather than just the one that's clicked. Hopefully that makes sense. Please help! Thanks in advance.

import React, { useState, useEffect } from 'react';
import './index.css';

const url = 'https://course-api.com/react-tours-project';

const Tour = () => {
    const [tourItem, setTourItem] = useState('');

    const removeItem = (id) => {
        let newList = tourItems.filter((item) => item.id !== id);
        setTourItem(newList);
    };
    const [fetchingData, setFetchingData] = useState(true);

    useEffect(() => {
        const abortController = new AbortController();
        const fetchUrl = async () => {
            try {
                const response = await fetch(url, {
                    signal: abortController.signal,
                });
                if (fetchingData) {
                    const data = await response.json();
                    setTourItem(data);
                }
                setFetchingData(false);
            } catch (e) {
                console.log(e);
            }
        };
        fetchUrl();
        return () => {
            //cleanup!
            abortController.abort();
        };
    });

    const tourItems = Object.values(tourItem);

    const [readMore, setReadMore] = useState(false);

    return (
        <>
            {tourItems.map((item) => {
                return (
                    <div key={item.id}>
                        <article className='single-tour'>
                            <img src={item.image} alt={item.name} />
                            <footer>
                                <div className='tour-info'>
                                    <h4>{item.name}</h4>
                                    <h4 className='tour-price'>
                                        ${item.price}
                                    </h4>
                                </div>
                                {readMore ? (
                                    <p>
                                        {item.info}
                                        <button
                                            onClick={() => setReadMore(false)}
                                        >
                                            Show Less
                                        </button>
                                    </p>
                                ) : (
                                    <p>
                                        {item.info.slice(0, 450) + '...'}
                                        <button
                                            onClick={() => setReadMore(true)}
                                        >
                                            Read More
                                        </button>
                                    </p>
                                )}
                                <button
                                    className='delete-btn'
                                    onClick={() => removeItem(item.id)}
                                >
                                    Not Interested
                                </button>
                            </footer>
                        </article>
                    </div>
                );
            })}
        </>
    );
};

export default Tour;

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Good question! It happened because you share the readMore state with all of the tour items. You can fix this by encapsulating the tour items into a component.

It should look something like this;

The component that encapsulates each tour items

import React, {useState} from "react";
import "./index.css";

const SpecificTourItems = ({item, removeItem}) => {
    const [readMore, setReadMore] = useState(false);
    return (
      <div key={item.id}>
        <article className="single-tour">
          <img src={item.image} alt={item.name} />
          <footer>

            <div className="tour-info">
              <h4>{item.name}</h4>
              <h4 className="tour-price">${item.price}</h4>
            </div>
            {readMore ? (
              <p>
                {item.info}
                <button onClick={() => setReadMore(false)}>Show Less</button>
              </p>
            ) : (
              <p>
                {item.info.slice(0, 450) + "..."}
                <button onClick={() => setReadMore(true)}>Read More</button>
              </p>
            )}
            <button className="delete-btn" onClick={() => removeItem(item.id)}>
              Not Interested
            </button>
          </footer>
        </article>
      </div>
    );
  };

  export default SpecificTourItems;

the component that fetch & maps all the tour items (your old component :))

import React, {useState, useEffect} from "react";
import SpecificTourItems from "./SpecificTourItems";

const url = "https://course-api.com/react-tours-project";

const Tour = () => {
  const [tourItem, setTourItem] = useState("");

  const removeItem = (id) => {
    let newList = tourItems.filter((item) => item.id !== id);
    setTourItem(newList);
  };
  const [fetchingData, setFetchingData] = useState(true);

  useEffect(() => {
    const abortController = new AbortController();
    const fetchUrl = async () => {
      try {
        const response = await fetch(url, {
          signal: abortController.signal,
        });
        if (fetchingData) {
          const data = await response.json();
          setTourItem(data);
        }
        setFetchingData(false);
      } catch (e) {
        console.log(e);
      }
    };
    fetchUrl();
    return () => {
      //cleanup!
      abortController.abort();
    };
  });

  const tourItems = Object.values(tourItem);

  const [readMore, setReadMore] = useState(false);

  return (
    <>
      {tourItems.map((item, key) => {
        return (
          <SpecificTourItems item={item} removeItem={removeItem} key={key} />
        );
      })}
    </>
  );
};

export default Tour;

I hope it helps, this is my first time answering question in Stack Overflow. Thanks & Good luck!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

56.5k users

...