React Hooks: запоминать обработчики кликов для списка?

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

Мой код выглядит примерно так:

function ItemList({items}) {
  const [isDropdownExpanded, setIsDropdownExpanded] = useState(null);
  const itemClickHandlers = useMemo(() => {
    const handlers = {}
    items.forEach(item => {
      handlers[item.id] = value => setIsDropdownExpanded(value ? item.id : null);
    });
    return handlers;
  }, [items.map(item => item.id).join(',')]);
}
return (
  <ul>
  {items.map(item =>
    <li onClick={itemClickHandlers[item.id]}>
      <Item name={item.name} isDropdownExpanded={isDropdownExpanded === item.id}/>
    </li>
  )}
  </ul>
);

Приведенный выше код работает, но выводит ненужные изображения, когда пользователь добавляет или удаляет элемент из списка. (itemClickHandlers генерирует новый объект со всеми новыми обработчиками, даже если должен быть создан или удален только один обработчик, поэтому каждый элемент в списке перерисовывается, потому что isDropdownExpanded не будет равен по ссылочному равенству.)

Желательно использовать только функциональные компоненты React, как я могу отобразить список элементов, подобных этому, с минимальным повторным отображением при изменении списка элементов?

2
задан Ceasar Bautista 26 June 2019 в 23:10
поделиться

1 ответ

Вам не нужен обработчик для каждого элемента в массиве. Все, что вам нужно, это иметь один обработчик, которому вы передаете идентификатор, который можно получить из события, если вы добавите идентификатор в элемент li

function ItemList({items}) {
  const [isDropdownExpanded, setIsDropdownExpanded] = useState(null);
  const itemClickHandlers = useCallback((e) => {
      const id = e.currentTarget.id;
      setIsDropdownExpanded(prev => (prev != id ? id: null));
  }, [])

    return (
      <ul>
      {items.map(item =>
        <li key={item.id} id={item.id} onClick={itemClickHandler}>
          <Item name={item.name} isDropdownExpanded={isDropdownExpanded === item.id}/>
        </li>
      )}
      </ul>
    );
}
2
ответ дан Shubham Khatri 26 June 2019 в 23:10
поделиться
  • 1
    I' m довольный, что Вы упомянули наконец, блок мог бы бросить; it' s вещь, что большинство " используйте RAII" ответы, кажется, игнорируют. Чтобы избежать необходимости писать наконец блок дважды, Вы могли сделать что-то как std::exception_ptr e; try { /*try block*/ } catch (...) { e = std::current_exception(); } /*finally block*/ if (e) std::rethrow_exception(e); – sethobrien 23 March 2015 в 17:14
  • 2
    I' m довольный, что Вы упомянули наконец, блок мог бы бросить; it' s вещь, что большинство " используйте RAII" ответы, кажется, игнорируют. Чтобы избежать необходимости писать наконец блок дважды, Вы могли сделать что-то как std::exception_ptr e; try { /*try block*/ } catch (...) { e = std::current_exception(); } /*finally block*/ if (e) std::rethrow_exception(e); – sethobrien 23 March 2015 в 17:14
  • 3
    I' m довольный, что Вы упомянули наконец, блок мог бы бросить; it' s вещь, что большинство " используйте RAII" ответы, кажется, игнорируют. Чтобы избежать необходимости писать наконец блок дважды, Вы могли сделать что-то как std::exception_ptr e; try { /*try block*/ } catch (...) { e = std::current_exception(); } /*finally block*/ if (e) std::rethrow_exception(e); – sethobrien 23 March 2015 в 17:14
  • 4
    I' m довольный, что Вы упомянули наконец, блок мог бы бросить; it' s вещь, что большинство " используйте RAII" ответы, кажется, игнорируют. Чтобы избежать необходимости писать наконец блок дважды, Вы могли сделать что-то как std::exception_ptr e; try { /*try block*/ } catch (...) { e = std::current_exception(); } /*finally block*/ if (e) std::rethrow_exception(e); – sethobrien 23 March 2015 в 17:14
  • 5
    I' m довольный, что Вы упомянули наконец, блок мог бы бросить; it' s вещь, что большинство " используйте RAII" ответы, кажется, игнорируют. Чтобы избежать необходимости писать наконец блок дважды, Вы могли сделать что-то как std::exception_ptr e; try { /*try block*/ } catch (...) { e = std::current_exception(); } /*finally block*/ if (e) std::rethrow_exception(e); – sethobrien 23 March 2015 в 17:14
  • 6
    I' m довольный, что Вы упомянули наконец, блок мог бы бросить; it' s вещь, что большинство " используйте RAII" ответы, кажется, игнорируют. Чтобы избежать необходимости писать наконец блок дважды, Вы могли сделать что-то как std::exception_ptr e; try { /*try block*/ } catch (...) { e = std::current_exception(); } /*finally block*/ if (e) std::rethrow_exception(e); – sethobrien 23 March 2015 в 17:14
  • 7
    I' m довольный, что Вы упомянули наконец, блок мог бы бросить; it' s вещь, что большинство " используйте RAII" ответы, кажется, игнорируют. Чтобы избежать необходимости писать наконец блок дважды, Вы могли сделать что-то как std::exception_ptr e; try { /*try block*/ } catch (...) { e = std::current_exception(); } /*finally block*/ if (e) std::rethrow_exception(e); – sethobrien 23 March 2015 в 17:14
  • 8
    I' m довольный, что Вы упомянули наконец, блок мог бы бросить; it' s вещь, что большинство " используйте RAII" ответы, кажется, игнорируют. Чтобы избежать необходимости писать наконец блок дважды, Вы могли сделать что-то как std::exception_ptr e; try { /*try block*/ } catch (...) { e = std::current_exception(); } /*finally block*/ if (e) std::rethrow_exception(e); – sethobrien 23 March 2015 в 17:14
  • 9
    I' m довольный, что Вы упомянули наконец, блок мог бы бросить; it' s вещь, что большинство " используйте RAII" ответы, кажется, игнорируют. Чтобы избежать необходимости писать наконец блок дважды, Вы могли сделать что-то как std::exception_ptr e; try { /*try block*/ } catch (...) { e = std::current_exception(); } /*finally block*/ if (e) std::rethrow_exception(e); – sethobrien 23 March 2015 в 17:14
Другие вопросы по тегам:

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