javascript 属性更改时如何重新渲染反应组件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/50618398/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to re-render react component when a property changes
提问by Luis Valencia
so I have this react component, with a dropdown property (SPFx) which has 2 values, I need that when the dropdown is changed the react is re-rendered again, the dropdown defines the datasource from where the values will be retrieved.
所以我有这个 react 组件,它有一个下拉属性 (SPFx),它有 2 个值,我需要当下拉列表更改时,react 再次重新呈现,下拉列表定义了从中检索值的数据源。
Webpart.ts
Webpart.ts
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneDropdown
} from "@microsoft/sp-webpart-base";
import * as strings from 'AbstractfactoryWebPartStrings';
import Abstractfactory from './components/Abstractfactory';
import { IAbstractFactoryProps } from './components/IAbstractFactoryProps';
import { IAbstractfactoryWebPartProps } from "./IAbstractfactoryWebPartProps";
export default class AbstractfactoryWebPart extends BaseClientSideWebPart<IAbstractfactoryWebPartProps> {
public render(): void {
const element: React.ReactElement<IAbstractFactoryProps > = React.createElement(
Abstractfactory,
{
datasource: this.properties.datasource
}
);
ReactDom.render(element, this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void {
super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneDropdown("datasource", {
label: "DataSource",
options: [
{ key: "1", text: "Sharepoint"},
{ key: "2", text: "JSON" }
],
selectedKey: "1",
})
]
}
]
}
]
};
}
}
Component.tsx
组件.tsx
import * as React from 'react';
import { IAbstractFactoryProps } from "./IAbstractFactoryProps";
import { IAbstractFactoryState } from "./IAbstractFactoryState";
import styles from './Abstractfactory.module.scss';
import { escape } from '@microsoft/sp-lodash-subset';
import DaoFactory from "./DaoFactory";
import ICustomerDao from "./ICustomerDao";
import DataSources from "./DatasourcesEnum";
export default class Abstractfactory extends React.Component<IAbstractFactoryProps, IAbstractFactoryState> {
//Private instance of customerDao, please note it returns ICustomerDao, an Interface,
//not a concrete type
private customerDao: ICustomerDao;
constructor(props: IAbstractFactoryProps, state: IAbstractFactoryState) {
super(props);
this.setInitialState();
// We set the Dao depending on the selected data source
this.setDaos(props.datasource);
//Then we set the list of customers and note, we dont care if they come from Sharepoint
//Rest API or anything else.
this.state = {
items: this.customerDao.listCustomers(),
};
}
public render(): React.ReactElement<IAbstractFactoryProps> {
return (
<div className={ styles.abstractfactory }>
<div className={ styles.container }>
<div className={ styles.row }>
<div className={ styles.column }>
{this.state.items.map( i => (<div key={i.id}>{i.firstName}</div>))}
</div>
</div>
</div>
</div>
);
}
public setInitialState(): void {
this.state = {
items: []
};
}
private setDaos(datasource: string): void {
const data: DataSources = datasource === "Sharepoint" ? DataSources.SharepointList : DataSources.JsonData;
this.customerDao = DaoFactory.getDAOFactory(data).getCustomerDAO();
//Now, its transparent for us a UI developers what datasource was selected
//this.customerDao.
}
}
State
状态
import Customer from "./Customer";
export interface IAbstractFactoryState {
items?: Customer[];
}
DaoFactory
刀厂
import ICustomerDAO from "./ICustomerDAO";
import DataSources from "./DatasourcesEnum";
abstract class DAOFactory {
//For each entity we will need to implement getCustomerDAO, this will make it easily replaceable
//when another datasource comes in
public abstract getCustomerDAO(): ICustomerDAO;
//Static method that receives a parameter depending on the datasource and will return the specifc
//factory
public static getDAOFactory(whichFactory: DataSources): DAOFactory {
switch (whichFactory) {
case DataSources.SharepointList:
return new SharepointListDAOFactory();
case DataSources.JsonData:
return new JsonDAOFactory();
default :
return null;
}
}
}
export default DAOFactory;
import SharepointListDAOFactory from "./SharepointListDAOFactory";
import JsonDAOFactory from "./JsonDAOFactory";
JsonCustomerDao.ts
JsonCustomerDao.ts
import ICustomerDao from "./ICustomerDao";
import Customer from "./Customer";
class JsonCustomerDAO implements ICustomerDao{
public insertCustomer(): number {
// implementation to be done by reader
return 1;
}
public deleteCustomer(): boolean {
// implementation to be done by reader
return true;
}
public findCustomer(): Customer {
// implementation to be done by reader
return new Customer();
}
public updateCustomer(): boolean {
// implementation to be done by reader
return true;
}
public listCustomers(): Customer[] {
// implementation to be done by reader
let c1: Customer= new Customer();
let c2: Customer= new Customer();
c1.id="3";
c1.firstName="Andrew";
c1.lastName="Valencia";
c2.id="4";
c2.firstName="Charles";
c2.lastName="Smith";
let list: Array<Customer> = [c1, c2 ];
return list;
}
}
export default JsonCustomerDAO;
SharepointCustomerDao
SharepointCustomerDao
import ICustomerDao from "./ICustomerDao";
import Customer from "./Customer";
class SharepointCustomerDao implements ICustomerDao {
public insertCustomer(): number {
// implementation to be done by reader
return 1;
}
public deleteCustomer(): boolean {
// implementation to be done by reader
return true;
}
public findCustomer(): Customer {
// implementation to be done by reader
return new Customer();
}
public updateCustomer(): boolean {
// implementation to be done by reader
return true;
}
public listCustomers(): Customer[] {
// implementation to be done by reader
let c1: Customer = new Customer();
c1.id="1";
c1.firstName="Luis";
c1.lastName="Valencia";
let c2: Customer = new Customer();
c2.id="2";
c2.firstName="John";
c2.lastName="Smith";
let list: Array<Customer> = [c1, c2 ];
return list;
}
}
export default SharepointCustomerDao;
The first time its executed the values are rendered for the default datasource, but when the property changes, the UI is not changing with the new values.
第一次执行时,会为默认数据源呈现值,但是当属性更改时,UI 不会随新值而更改。
I found that I can use this event to set the new props datasource value when the dropdown changes
我发现我可以使用此事件在下拉列表更改时设置新的道具数据源值
protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void {
this.properties[this.properties.datasource] = newValue;
this.render();
super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
}
However the view is not re-rendered, can I use any of the react js events to re-render or re-set the status when a prop is changed?
但是,视图不会重新渲染,我可以使用任何 react js 事件在更改道具时重新渲染或重新设置状态吗?
采纳答案by noa-dev
The Reacts way to rerender a component on data change is to updates it's state.
在数据更改时重新渲染组件的 Reacts 方法是更新它的状态。
For instance if you have a component <Foo />which contains a rendermethod to display it's state like
例如,如果您有一个<Foo />包含渲染方法的组件来显示它的状态,如
...
render() {
return(
<div>Hello {this.state.name}</div>
)
}
...
The component will display Hello and whetever is in the state.name
该组件将显示 Hello并且无论在 state.name 中
A re-render happens everythime you update the state of the given component. So in your case if you get new items, you have to push them into the state. Once that happens the react class will trigger its render method and will use the current state to display new data.
每次更新给定组件的状态时都会发生重新渲染。因此,在您的情况下,如果您获得新项目,则必须将它们推入状态。一旦发生这种情况,react 类将触发其渲染方法并使用当前状态来显示新数据。
For that to work you would need a custom method which is being called from outside or within your component.
为此,您需要一个从组件外部或内部调用的自定义方法。
For instance
例如
...
foo(receivesSomething) {
const items = this.state.items;
items.push(receivesSomething);
this.setState({items}); // once a setState is being executed in any component react will make a diff between the current DOM and the ShadowDOM, if something is different then it will trigger a re-render of the affected component
}
...
This article is quite nicely written http://lucybain.com/blog/2017/react-js-when-to-rerender/and explains it a bit as well. I would recommend you to look into the lifecycle methods in general as well.
这篇文章写得很好http://lucybain.com/blog/2017/react-js-when-to-rerender/并对其进行了一些解释。我建议您也研究一下一般的生命周期方法。
update
更新
For instance like this.
比如像这样。
class Foo extends React.Component {
constructor(props){
super();
window.someFunction = payload => {
// do whatever you want to do with the payload here
this.setState({ items: payload });
}
}
}
update2
更新2
If your component is listening / receiving props you can use the componentWillReceiveProps(newProps)lifecycle method from react and update your component state within that
如果您的组件正在侦听/接收道具,您可以使用componentWillReceiveProps(newProps)来自 react的生命周期方法并在其中更新您的组件状态

