@Multiple_Illusionsi

Манипуляции со state и несколько вопросов по рендерингу и архитектуре в реакт?

Здравствуйте! Долго не хотел просить помощи, искал сам, но из-за отсутствия нужных знаний вынужден сдаться и написать сюда

Осваиваю реакт и решил создать подобие интернет-магазина на основе данных, полученных с API, но запнулся на некоторых моментах

Приложение работает по такому принципу:
Идет загрузка данных с API, эти данные загружаем в state = { result: [] }, затем рендерим эти данные, вытягивая данные из state и пропуская их через компонент (который стилизует данные). В итоге мы имеем на странице 25 стилизованных компонентов с данными, и я хочу чтобы при нажатии кнопки на этом компоненте он добавлялся в корзину state = { basket: [] } .
Вот так это выглядит
spoiler
5c9c0d07857c2335234651.jpeg

Теперь к коду (который я немного упрощу, оставив базовый функционал) и затем к проблемам и возникшим вопросам

Компонент для стилизации (возможно будет больно глазам, простите)
spoiler
const NewsItem = ({ styles, id, name, tagline, abv, image, clickFilter } ) => (
  <div>
     <ul className={styles}>
       <li> <img src={image} alt="empty"
       height='150'></img> </li>
       <li> {`ID - ${id}`} </li>
       <li> {`Название - ${name}`} </li>
       <li> {`Тэг - ${tagline}`} </li>
       <li> {`Крепость - ${abv}`} </li>
        <li>
        <button onClick={clickFilter}>Добавить в корзину</button> //при нажатии этой кнопки добавление в корзину
        </li>       
      </ul>
  </div>
);


Основной файл
spoiler
const BASE_PATH = 'https://api.punkapi.com/v2/beers';

class Store extends Component {
  state = {
    result: [],
    buy:[],
   }

componentDidMount() { 
  fetch(`${BASE_PATH}`) 
    .then(res => res.json())                   //это работает
    .then(result => this.setItem(result))
    .catch(error => error);     
}

setItem = result => {         
  this.setState(  {result}  ); 
}

render() {

  const { result } = this.state;

  return (
    <div>         
       <ul >
        {result.map(({ id, name, tagline, abv, description, image_url}) =>   // вытягивание полученных с 
            <NewsItem key={id}                                              // АПИ данных  и их стилизация
              styles={'item'}             
              image={image_url}    // NewsItem - компонент для стилизации
              id={id}
              name={name}
              tagline={tagline}
              abv = {abv}
              // clickFilter = {здесь нужна реализация метода для добавления в корзину}             
              />
          )}
       </ul>       
      </div>
  )
}


А теперь вопросы:

1. Каким образом добавить полученные "товары" в корзину? Я не смог использовать значения из state.result для последующего их добавления в state.basket.
result это просто массив из объектов. Можно получить доступ к любому товару с помощью this.state.result[индекс_объекта], но ничего с этими данными напрямую делать у меня не получается, как и с помощью setState

2. Вопрос вытекает из 1го, а нормальная ли это практика добавлять в state данные, полученные из API? Если нет, то где тогда можно их хранить?

3. Я хотел создать у каждого "товара" кнопку, которая будет менять стиль КОНКРЕТНО этого товара (это не несет никакой смысловой нагрузки, но мне хотелось это реализовать чтобы понять некоторые вещи).
У моего компонента NewsItem есть пропс styles, который устанавливает стиль в компонент.
const NewsItem = ({ styles, ...остальные пропсы}) => (
  <div >
     <ul className={styles}>
...код дальше
 </div>

Пытался поменять className={ something === true ? 'class1' : 'class2' }
Создал кнопку с обработчиком onClick = { something === false } . Это псевдокод, но смысл понятен
В итоге максимум что у меня получилось, это смена стиля ВСЕХ товаров при нажатии на кнопку в любом товаре. (это было сделано с помощью true/false в state и обработчика который менял значение в state, а вместе с ним и менялся стиль всех компонентов).
Я примерно понимаю что эти вопросы можно решить с помощью пропсов или смены архитектуры, но я решил не тратить еще пару дней на поиски ответов, а попросить помощи

Спасибо большое что выслушали меня, надеюсь на помощь :)
  • Вопрос задан
  • 87 просмотров
Решения вопроса 1
rockon404
@rockon404 Куратор тега React
Frontend Developer
1. Что с именами? NewsItem, setItem, result, clickFilter, styles, buy - никогда не пишите подобного даже в шутку. Замените: NewsItem на Product(или ShopItem), setItem - вообще удалите, result на products, clickFilter на handleAddToCartClick, styles на className, buy на cart.

2. Нет никакого смысла передавать в ShopItem каждое свойство товара по отдельности. Только усложняете анализ вашего кода. Лучше:
{products.map(product => (
  <ShopItem
    key={product.id}
    className={'item'}
    item={product}
    onAddToCartClick={this.handleAddToCartClick}
  />
)}


3. Base path на то и base path, что не должен содержать конкретный эндпоинт. Правильно:
const BASE_PATH = 'https://api.punkapi.com/v2';

/* ... */

fetch(`${BASE_PATH}/beers`)


Каким образом добавить полученные "товары" в корзину? Я не смог использовать значения из state.result для последующего их добавления в state.basket.
result это просто массив из объектов. Можно получить доступ к любому товару с помощью this.state.result[индекс_объекта], но ничего с этими данными напрямую делать у меня не получается, как и с помощью setState

Если вы не понимаете как работать с данными(массивы, объекты), то вам стоит сейчас заострить свое внимание на изучении этого вопроса:
JavaScript: Структуры данных
Вам вообще нет смысла копировать товар в массив cart, туда можно складывать только id товара и количество:
class Store extends Component {
  state = {
    products: [],
    cart:[],
  };

  handleAddToCartClick = (id, quantity) => {
    const { cart } = this.state;
    const itemIndex = cart.findIndex(item => item.id === id);

    if (itemIndex !== -1) {
      const newCart = [...cart];
      newCart[itemIndex] = { id, quantity: cart[itemIndex].quantity + quantity };
      this.setState({ cart: newCart });
    } else {
      this.setState(prevState => ({
        cart: [
          ...prevState.cart,
          { id, quantity },
        ],
      }));
    }
  };

const ShopItem = ({ className, item, onAddToCartClick }) => (
  <div>
    {/* ... */}
    <button onClick={() => onAddToCartClick(item.id, 1)}>Добавить в корзину</button>
  <div/>
);

Вопрос вытекает из 1го, а нормальная ли это практика добавлять в state данные, полученные из API? Если нет, то где тогда можно их хранить?

По-хорошему, в redux store. redux стоит использовать тут хотя бы потому, что те же данные о товарах понадобятся и в корзине, и на странице checkout.

Я хотел создать у каждого "товара" кнопку, которая будет менять стиль КОНКРЕТНО этого товара (это не несет никакой смысловой нагрузки, но мне хотелось это реализовать чтобы понять некоторые вещи).
У моего компонента NewsItem есть пропс styles, который устанавливает стиль в компонент.

Самый простой вариант использовать хук useState:
import React, { useState } from 'react';

const ShopItem = ({ className, item, onAddToCartClick } ) => {
  const [isActive, setIsActive] = useState(false);
  
  handleToggleActiveClick = () => {
    setIsActive(!isActive);
  };

  return (
    <div>
      <ul className={`${className}${isActive ? ' active' : ''}`}>
        {/* ... */} 
        <button onClick={handleToggleActiveClick}>Toggle active</button>
      </ul>
    <div/>
  );
}
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы