属性更改时如何重新渲染反应组件

Posted

技术标签:

【中文标题】属性更改时如何重新渲染反应组件【英文标题】:How to re-render react component when a property changes 【发布时间】:2018-11-10 02:50:38 【问题描述】:

所以我有这个反应组件,它有一个下拉属性 (SPFx),它有 2 个值,我需要在更改下拉列表时再次重新呈现反应,下拉列表定义将从中检索值的数据源.

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",
                  )
              ]
            
          ]
        
      ]
    ;
  

组件.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.
    

状态

import Customer from "./Customer";

export interface IAbstractFactoryState   
    items?: Customer[];
  

道厂

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

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

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;

第一次执行时,值会为默认数据源呈现,但当属性更改时,UI 不会随着新值而更改。

我发现当下拉列表改变时我可以使用这个事件来设置新的 props 数据源值

 protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void 
    this.properties[this.properties.datasource] = newValue;


    this.render();

    super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
  

但是视图没有重新渲染,我可以使用任何 react js 事件来重新渲染或在 prop 更改时重新设置状态吗?

【问题讨论】:

【参考方案1】:

Reacts 在数据更改时重新渲染组件的方式是更新它的状态。

例如,如果您有一个组件 &lt;Foo /&gt;,其中包含一个渲染方法来显示它的状态

...
render() 
   return(
     <div>Hello this.state.name</div>
   )

...

组件将显示 Hello 并且无论是在 state.name 中

每次更新给定组件的状态时都会重新渲染。因此,在您的情况下,如果您获得新项目,则必须将它们推入状态。一旦发生这种情况,react 类将触发其渲染方法并使用当前状态显示新数据。

为此,您需要一个从组件外部或内部调用的自定义方法。

例如

...
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

... 

这篇文章写得很好http://lucybain.com/blog/2017/react-js-when-to-rerender/ 并解释了一点。我建议您也大致研究一下生命周期方法。

更新

比如这样。

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 );
      

   

更新2

如果您的组件正在侦听/接收道具,您可以使用 react 中的 componentWillReceiveProps(newProps) 生命周期方法并在其中更新您的组件状态

【讨论】:

我明白,问题是这是 Sharepoint 框架,我有一个带有一个下拉属性的 webpart,然后 webpart 有 react 组件,有一个名为 onPropertyPaneFieldChanged 的​​事件我可以放任何逻辑,但我如何在那里传递新状态?或者我如何触发构造函数再次执行,不确定它是否清楚我的问题。 javascript OOP 编程中,构造函数只在类被实例化时被调用一次。因此不能再次调用它。您可以做的是在构造函数中创建一个方法,或者在您创建全局方法的 compoentDidMount 中创建一个可以从外部访问的方法。例如window.something = () => ;并使用该方法,您会收到任何更新,然后在那里添加 setState 构造函数中的方法?不知道我是否理解这一点。 更新了我的答案。对不起,我在工作,有时不得不缩短它^^ 不确定它是否会工作 属性 'someFunction' 在类型 'Window' 上不存在。

以上是关于属性更改时如何重新渲染反应组件的主要内容,如果未能解决你的问题,请参考以下文章

如何在反应原生的子组件上重新渲染父组件?

反应输入文本失去对重新渲染的关注

在重新渲染时反应不更新属性

对道具更改做出反应重新渲染

在反应中的 url 更改上重新渲染相同的组件

即使道具没有改变,为啥还要对重新渲染组件做出反应?