Сделать "лайки" в react js

Всем доброго утра) Моя задача заключается в том,что бы выводить на экран карточки-полученные через API c помощью axios. Cделать так,чтобы они лайкались и сохраняли свое состояние.

Что я сделал Я создал функцию

  
const handleLike = id => {
  cards(state => state.map(el => {
     if (el.id === id) {
         return {
             ...el, 
             liked: !el.liked,
         }
     }
     return el;
  }))
}
   

,которая меняет состояние и тем самым я добавляю модификатор liked к классу place-card__like-icon. Так же я передаю состояние liked классу place-card. Делаю это для того, чтобы при нажатие на кнопку я получил все "залайканые" карточки.

Что то пошло не так Почему то не работает код.

import React,{useState,useEffect} from 'react';
import axios from 'axios';
function App() {
 
  const [isDisplay, setDisplay] = React.useState(false);
  const displayAdd=()=>setDisplay(!isDisplay);
 
const [appState, setAppState] = useState(
      {
        allCards:[]
      
      }
    )
  
   useEffect(() => {
      setAppState({loading: true})
      const apiUrl = 'https://test-api-921f7-default-rtdb.firebaseio.com/cards.json';
      axios.get(apiUrl).then((resp) => {
        const allCards = resp.data;
        setAppState({
         
         allCards
         });
      });
    }, [setAppState]);
 

    const likeButtonHandler = (id) => {
      setAppState(
        appState.map((item) =>
          item.id === id ? { ...item, liked: !item.liked } : item
        )
      );
    };


  return (
  <div className="app">
  
  <div>
      {appState.map(({ id, name, liked = false }) => (
        <div key={id} style={{border: '1px solid black', margin: '1rem', padding: '1rem'}}>
          <div>Name: {name}</div>
          <div>Liked: {liked.toString()}</div>
          <button onClick={() => likeButtonHandler(id)}>
            {liked ? "unlike me" : "like me"}
          </button>
        </div>
      ))}
    </div>
  
   
   
</div>
  );


      }


export default App;


Ответы (1 шт):

Автор решения: Konstantin Modin

Хук useState работает не так как стейт в классе. Поэтому заводим отдельный хук-стейт на данных из axios (allCards - массив) и отдельный хук-стейт для loading-flag, для простоты и впрочем так это делается. Когда нажимается кнопка лайк - проходим по всему массиву allCards и обновляем состояние только в элементе с заданным ID, далее обновляем состояние, реакт всё перерисовывает. Во время загрузки данных можно отображать loading-spinner. У меня получилось примерно так:

// import React from "react";
// import axios from "axios";

function App() {
  // const [isDisplay, setDisplay] = React.useState(false);
  // const displayAdd = () => setDisplay(!isDisplay);

  const [allCards, setAllCards] = React.useState([]);
  const [loading, setLoading] = React.useState(false);

  React.useEffect(() => {
    setLoading(true);

    const apiUrl =
      "https://test-api-921f7-default-rtdb.firebaseio.com/cards.json";
    axios.get(apiUrl).then((resp) => {
      const allCards = resp.data;
      setAllCards(allCards);
      setLoading(false);
    });
  }, []);

  const likeButtonHandler = (id) => {
    setAllCards(
      allCards.map((item) =>
        item.id === id ? { ...item, liked: !item.liked } : item
      )
    );
  };

  return (
    <div className="app">
      <div>
        {loading
          ? "Loading..."
          : allCards.map(({ id, name, liked, link }) => (
              <div
                key={id}
                style={{
                  border: "1px solid black",
                  margin: "1rem",
                  padding: "1rem",
                }}
              >
                <div>Name: {name}</div>
                <div>Liked: {liked.toString()}</div>
                <img src={link} style={{ maxWidth: "100px" }} />
                <button onClick={() => likeButtonHandler(id)}>
                  {liked ? "unlike me" : "like me"}
                </button>
              </div>
            ))}
      </div>
    </div>
  );
}

// export default App;

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.4/axios.min.js" integrity="sha512-lTLt+W7MrmDfKam+r3D2LURu0F47a3QaW5nF0c6Hl0JDZ57ruei+ovbg7BrZ+0bjVJ5YgzsAWE+RreERbpPE1g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>

→ Ссылка