数据类属性更改时使React组件重新呈现 [英] Make a React component rerender when data class property change
问题描述
在我的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屋!