Как перерисовать компонент реакции при изменении свойства

поэтому у меня есть этот компонент реакции с раскрывающимся свойством (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;

При первом выполнении значения отображаются для источника данных по умолчанию, но при изменении свойства пользовательский интерфейс не меняется с новыми значениями.

Я обнаружил, что могу использовать это событие для установки нового значения источника данных реквизита при изменении раскрывающегося списка.

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


    this.render();

    super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
  }

Однако представление не перерисовывается, могу ли я использовать какие-либо события js для повторного рендеринга или переустановки статуса при изменении реквизита?


person Luis Valencia    schedule 31.05.2018    source источник


Ответы (1)


Способ Reacts для повторного рендеринга компонента при изменении данных заключается в обновлении его состояния.

Например, если у вас есть компонент <Foo />, который содержит метод рендеринга для отображения его состояния, например

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

Компонент будет отображать Hello и все, что находится в state.name

Повторный рендеринг происходит каждый раз, когда вы обновляете состояние данного компонента. Итак, в вашем случае, если вы получаете новые элементы, вы должны поместить их в состояние. Как только это произойдет, класс реакции активирует свой метод рендеринга и будет использовать текущее состояние для отображения новых данных.

Для этого вам понадобится собственный метод, который вызывается извне или внутри вашего компонента.

Например

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

Если ваш компонент прослушивает/получает реквизиты, вы можете использовать метод жизненного цикла componentWillReceiveProps(newProps) из реакции и обновить состояние вашего компонента в этом

person noa-dev    schedule 31.05.2018
comment
Я понимаю, что проблема в том, что это Sharepoint Framework, где у меня есть веб-часть с одним выпадающим свойством, а затем веб-часть имеет компонент реакции, есть событие, называемое onPropertyPaneFieldChanged, где я могу поместить любую логику, но как мне передать там новое государство? или как мне вызвать повторное выполнение конструктора, не уверен, что мой вопрос понятен. - person Luis Valencia; 31.05.2018
comment
Как и в объектно-ориентированном программировании JavaScript, конструктор вызывается только один раз при создании экземпляра класса. Поэтому он не может быть вызван снова. Что вы можете сделать, так это создать метод внутри конструктора или compoentDidMount, где вы создаете глобальный метод, доступный извне. например window.something = () =› {}; и с помощью этого метода вы получаете любое обновление, а затем добавляете туда setState - person noa-dev; 31.05.2018
comment
метод внутри конструктора? не уверен, понял ли я это. - person Luis Valencia; 31.05.2018
comment
Обновил мой ответ. Извините, я на работе, иногда приходится прерываться ^^ - person noa-dev; 31.05.2018
comment
не уверен, что это сработает. Свойство «someFunction» не существует для типа «Window». - person Luis Valencia; 31.05.2018
comment
Почему бы и нет? Окно является глобальным объектом среды браузера. Вы можете прикрепить к нему столько ключей, сколько захотите. jsfiddle.net/smajn8L4 - person noa-dev; 31.05.2018
comment
да, я знаю это, но он не распознается компилятором, я думаю, вероятно, потому, что это не проект на 100%, его структура sharepoint реагирует - person Luis Valencia; 31.05.2018
comment
Да, вы можете использовать метод componentWillReceiveProps(newProps) для прослушивания изменений реквизита — попробуйте - person noa-dev; 31.05.2018
comment
Хорошо, я скажу это в своем ответе :) - person noa-dev; 31.05.2018