React — как сделать правильно работающие вычисляемые свойства?

Сейчас я пытаюсь сделать таблицу с флажками и столкнулся с очень раздражающей проблемой. Я воспроизвел эту проблему в этом примере.

У меня есть список элементов формата {id, value}. Для каждого элемента я создаю элемент div с флажком и текстом внутри. Когда я хочу сделать элемент выбранным, я добавляю идентификатор элемента в список selectedItems.
Проблема в том, что я не могу заставить флажок компонента обновлять свое состояние при изменении списка selectedItems.

Подскажите пожалуйста, как это сделать нормально? В Vue я мог просто создать вычисляемое свойство для каждого подкомпонента и передать selectedItems и связанную поддержку. Как это сделать в Реакте?

import {Checkbox} from "@mui/material";
import {useMemo, useState} from "react";

export default function Example() {
  const items = useState([{id: 0, value: "Item 1"}, {id: 1, value: "Item 2"}, {id: 2, value: "Item 3"}])
  const [selectedItems, setSelectedItems] = useState([])
  console.info(items)

  function ListItems({items}) {
    return useMemo(() => {
      const elements = []
      for (let i = 0; i < items[0].length; i++) {
        const item = items[0][i]
        const checked = selectedItems.indexOf(item.id) !== -1
        console.info(item, items.length)
        elements.push(
          <div key = {i}>
            <Checkbox
              checked = {checked}
              onChange = {() => {
                let state = selectedItems
                if (checked) {
                  state = state.filter(id => id !== item.id)
                } else {
                  state.push(item.id)
                }
                setSelectedItems(state)
              }}
            />
            <p>{item.value}</p>
          </div>
        )
      }

      return elements
    }, [items])
  }

  return (
    <div style = {{display: "flex", flexDirection: "column"}}>
      <ListItems items = {items}/>
    </div>
  )
}

Codesandbox: https://codesandbox.io/s/not-working-computed-props-react-forked-fywtxp?file=/src/App.js

Вы можете загрузить этот код куда-нибудь, чтобы мы могли проверить, что с ним не так 🙏

Farbod Shabani 22.11.2022 17:15

Ваш useMemo зависит от items, но не от selectedItems, поэтому он не переоценивается, вы должны сделать useMemo(..., [items, selectedItems]), чтобы при изменении одного из items или selectedItems он переоценивал

Nikita Chayka 22.11.2022 17:25

Да, ты прав. Я действительно пропустил это на этот раз. Но должен сказать, что раньше у меня вообще не было useMemo, и это не влияло... Сейчас исправил, все равно не работает...

untured 22.11.2022 17:28
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
3
136
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Помимо моего комментария о selectedItems, который должен быть предоставлен в useMemo зависимостях, у вас есть другая проблема, которая заключается в том, что вы действительно не изменяете свое состояние здесь setSelectedItems(state)

Массивы сравниваются по ссылке, поэтому если у вас есть const x = y = []; и тогда x.push('1'), x по-прежнему равно y. Следовательно, когда вы делаете

let state = selectedItems
...
state.push(...)
...
setSelectedItems(state)

Это не вызывает повторный рендеринг, потому что state все еще равен предыдущему (до selectedItems)

Исправление может быть либо

state = [...state, item.id] вместо push

Или setSelectedItems([...state])

Другие вопросы по теме