数据类属性更改时使React组件重新呈现 [英] Make a React component rerender when data class property change

查看:34
本文介绍了数据类属性更改时使React组件重新呈现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的Typescript应用程序中,有一个代表某些数据的类.此类是端到端共享的(前端和后端都使用它来构造数据).它具有一个名为 items 的属性,该属性是一个数字数组.

In my Typescript app, there's a class that represents some data. This class is being shared end to end (both front-and-back ends use it to structure the data). It has a property named items which is an array of numbers.

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

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

我正在尝试在组件中渲染这些数字,但是由于修改类实例不会导致重新渲染,因此我必须强制重新渲染"以生成新的 items 值渲染:

I'm trying to render those numbers in my component, but since modifying the class instance won't cause a re-render, I have to "force rerender" to make the new items values render:

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()将更改与组件同步.

While this works, it has one major drawback: addItem() is not the only modification done to INSTANCE; This class has actually around 10 to 15 properties that represent different data parts. So, doing forceUpdate() wherever a modification happens is a nightmare. Not no mention, if this instance will be modified outside the component, I won't be able to forceUpdate() to sync the change with the component.

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

Using useState([]) to represent items will solve this issue, but as I said Data has a lot of properties, so as some functions. That's another nightmare.

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

I would like to know what's the best way of rendering data from a class instance, without rerender hacks or unpacking the whole instance into a local component state.

谢谢!

这是一个Codesandbox演示,展示了使用类和本地状态.

Here's a Codesandbox demo that shows the differences between using a class and a local state.

推荐答案

以下是如何使数据实例可观察并在组件中使用Effect观察数据实例项中的更改的示例:

Here is an example of how you can make Data instance observable and use Effect in your components to observe changes in Data instance items:

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>

这篇关于数据类属性更改时使React组件重新呈现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆