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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 09:12:17  来源:igfitidea点击:

How to re-render react component when a property changes

javascriptreactjssharepointspfx

提问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的生命周期方法并在其中更新您的组件状态