数据类属性更改时使React组件重新呈现

时间:2020-04-10 14:58:19

标签: javascript reactjs typescript class

在我的Typescript应用程序中,有一个表示某些数据的类。此类是端到端共享的(前端和后端都使用它来构造数据)。它有一个名为 var EC = protractor.ExpectedConditions; browser.wait(EC.visibilityOf(rows), 500); 的属性,它是一个数字数组。

items

我正在尝试在组件中渲染这些数字,但是由于修改类实例不会导致重新渲染,因此我必须“强制重新渲染”以渲染新的class Data { constructor() { this.items = [0]; } addItem() { this.items = [...this.items, this.items.length]; } } 值:< / p>

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()的唯一修改;此类实际上具有大约10到15个代表不同数据部分的属性。因此,在任何发生修改的地方进行INSTANCE都是一场噩梦。不用说,如果将此实例在组件外部进行修改,我将无法forceUpdate()将更改与组件同步。

使用forceUpdate()表示useState([])将解决此问题,但是正如我所说的,items具有很多属性,还有一些功能。那是另一个噩梦。


我想知道从类实例渲染数据的最佳方法是什么,而无需重新渲染黑客或将整个实例解压缩为本地组件状态。

谢谢!

Here's a Codesandbox demo,显示了使用类和局部状态之间的区别。

1 个答案:

答案 0 :(得分:0)

下面是一个示例,该示例说明如何使数据实例可观察并在组件中使用效果以观察数据实例项中的更改:

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 <pre>{JSON.stringify(users)}</pre>;
};
const Products = () => {
  const products = useInstance(instance, getProducts);
  return <pre>{JSON.stringify(products)}</pre>;
};
const App = () => {
  const reset = () => {
    instance.reset();
    counter = 0;
  };
  return (
    <div>
      <button onClick={reset}>Reset</button>
      <div>
        users:
        <Users />
      </div>
      <div>
        products:
        <Products />
      </div>
    </div>
  );
};
ReactDOM.render(<App />, 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>