Реорганизовать компонент React при изменении свойства класса данных - PullRequest
2 голосов
/ 10 апреля 2020

В моем приложении Typescript есть класс, который представляет некоторые данные. Этот класс является общим для всех (оба интерфейса используют его для структурирования данных). У него есть свойство с именем items, которое представляет собой массив чисел.

class Data {
  constructor() {
    this.items = [0];
  }

  addItem() {
    this.items = [...this.items, this.items.length];
  }
}

Я пытаюсь отобразить эти числа в моем компоненте, но поскольку изменение экземпляра класса не вызовет повторную визуализацию Мне нужно «принудительно выполнить рендеринг», чтобы новые значения items отображались:

const INSTANCE = new Data();

function ItemsDisplay() {
  const forceUpdate = useUpdate(); // from react-use

  useEffect(() => {
    const interval = setInterval(() => {
      INSTANCE.addItem();
      forceUpdate(); // make it work
    }, 2000);

    return () => clearInterval(interval);
  }, []);

  return (
    <div>
      <h1>with class:</h1>
      <div>{INSTANCE.items.map(item => <span>{item}</span>)}</div>
    </div>
  );
}

Хотя это работает, у него есть один существенный недостаток: addItem() - не единственная модификация, сделанная для INSTANCE ; Этот класс на самом деле имеет от 10 до 15 свойств, которые представляют разные части данных. Таким образом, выполнение forceUpdate(), где бы ни происходила модификация, является кошмаром. Не говоря уже о том, что если этот экземпляр будет изменен вне компонента, я не смогу forceUpdate() синхронизировать c изменение с компонентом.

Использование useState([]) для представления items решит эту проблему, но, как я сказал, Data имеет много свойств, так же как и некоторые функции. Это еще один кошмар.


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

Спасибо!

Вот демонстрация Codesandbox , которая показывает различия между использованием класса и локальным состоянием.

1 Ответ

0 голосов
/ 10 апреля 2020

Вот пример того, как вы можете сделать наблюдаемый экземпляр данных и использовать Effect в своих компонентах для наблюдения изменений в элементах экземпляра данных:

const { useState, useEffect } = React;
class Data {
  constructor() {
    this.data = {
      users: [],
      products: [],
    };
    this.listeners = [];
  }

  addItem(type, newItem) {
    this.data[type] = [...this.data[type], newItem];
    //notify all listeners that something has been changed
    this.notify();
  }
  addUser(user) {
    this.addItem('users', user);
  }
  addProduct(product) {
    this.addItem('products', product);
  }
  reset = () => {
    this.data.users = [];
    this.data.products = [];
    this.notify();
  };
  notify() {
    this.listeners.forEach((l) => l(this.data));
  }
  addListener = (fn) => {
    this.listeners.push(fn);
    //return the remove listener function
    return () =>
      (this.listeners = this.listeners.filter(
        (l) => l !== fn
      ));
  };
}
const instance = new Data();
let counter = 0;
setInterval(() => {
  if (counter < 10) {
    if (counter % 2) {
      instance.addUser({ userName: counter });
    } else {
      instance.addProduct({ productId: counter });
    }
    counter++;
  }
}, 500);
//custom hook to use instance
const useInstance = (instance, fn = (id) => id) => {
  const [items, setItems] = useState(fn(instance.data));
  useEffect(
    () =>
      instance.addListener((items) => setItems(fn(items))),
    [instance, fn]
  );
  return items;
};
const getUsers = (data) => data.users;
const getProducts = (data) => data.products;
const Users = () => {
  const users = useInstance(instance, getUsers);
  return {JSON.stringify(users)}
; }; const Products = () => {const products = useInstance (экземпляр, getProducts); возврат
{JSON.stringify(products)}
; }; const App = () => {const reset = () => {instance.reset (); счетчик = 0; }; возврат (
Сброс
пользователей:
продукты:
); }; ReactDOM.render ( , document.getElementById ('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>


<div id="root"></div>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...