Question

I am using the SharePoint framework with React. I have a button on the web part that when clicked, I want to output a string on the web part. The way I currently have it set up, you have to refresh the page to get the value to show. The web part does not re-render automatically. Normally in React, I would set the state, which would trigger a re-render. With the SPFX, I don't see in this sample app, which I got online, where or how the state is being set. All I see is the interface properties being set.

This is the parent component. All the imports are removed for brevity.

export default class HelloWorldWebPart extends BaseClientSideWebPart<IHelloWorldProps> {

  public render(): void {
    const element: React.ReactElement<IHelloWorldProps > = React.createElement(
      HelloWorld, // this is the component HellowWorld.tsx containing the markup
      {
        PropertyOne: this.properties.PropertyOne,
        lists: this.properties.lists,
        getListData: this.getListData.bind(this)
      }
    );
    ReactDom.render(element, this.domElement);
  }

  // write a value to the web part
  private getListData(): void {
    this.properties.lists = `<p>The button worked!</p>`;
  }

  // set up the web part properties
  protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
    return {
      pages: [
        {
          header: {
            description: strings.PropertyPaneDescription
          },
          groups: [
            {
              groupName: strings.BasicGroupName,
              groupFields: [
                PropertyPaneTextField('PropertyOne', {
                  label: 'Property One',
                  onGetErrorMessage: this._validateTextFieldValue.bind(this)
                }),
              ]
            }
          ]
        }
      ]
    };
  }
}

The interface:

export interface IHelloWorldProps {
  PropertyOne: string;
  lists: any;
  getListData: () => void;
}

Finally, the chid component. All the imports are removed for brevity.

export default class HelloWorld extends React.Component<IHelloWorldProps, { BaseClientSideWebPart}> {
  constructor(props: IHelloWorldProps) {
    super(props);

    this._handleClick = this._handleClick.bind(this);
  }

  private _handleClick() {
    this.props.getListData();
  }

  public render(): React.ReactElement<IHelloWorldProps> {
    return (
      <div className={styles.helloWorld}>
        <div className={styles.container}>
          <div className={`ms-Grid-row ms-bgColor-themeDark ms-fontColor-white ${styles.row}`}>
            <div className="ms-Grid-col ms-lg10 ms-xl8 ms-xlPush2 ms-lgPush1">
              <span className="ms-font-xl ms-fontColor-white">React Web Part!</span><br />
              <button className="ms-Button ms-Button--primary root-82" onClick={this.props.getListData}>Get</button>
              <div dangerouslySetInnerHTML={{ __html: this.props.lists }} />
            </div>
          </div>
        </div>
      </div>
    );
  }
}
Was it helpful?

Solution

I believe this example is really not the best code I've seen.

To summarize the way SPFx works: - The parent component HelloWorldWebPart only exists to be loaded by the Framework and to handle the property pane. It should not send callback methods as props for the child component in most cases. - The child component is the one rendered as Root in the WebPart Zone. As every other React component, it's rendering is very smartly handled and automatically refreshed when its props or state is updated

If you want to display directly a property in the property pane, automatically re-rendered when changed, you only need to display it directly in the render() method of the child component:

public render(): React.ReactComponent<IHelloWorldProps> {
    return <div>{ this.props.PropertyOne }</div>
}

If you want your component to render specific things on its own behavior (buttons, loading data), you need a state. This state needs to be declared in the child component (since it is the one rendered) :

export default class HelloWorld extends React.Component<IHelloWorldProps,IHelloWorldState> {
    constructor(props: IHelloWorldProps) {
        super(props);
        this.state = { sampleStateProperty: '' };
    }
    ...

With the state interface being declared

export interface IHelloWorldState {
    sampleStateProperty: string
}

Now your button needs to changed the state of the component as a hello world example and the callback code is declared in the child component:

public buttonClicked () {
    this.setState({ sampleStateProperty: 'Hello World!' })
}

and the render method:

public render () {
    return <div>
        <button onClick={() => this.buttonClicked()}>Click me!</button>
        <div>{ this.state.sampleStateProperty }</div>
    </div>
}

I'd advise that you follow the sample projects for React and for SPFx to have a better grasp on the role of the different files and the concepts on React programming.

React Tic Tac Toe Tutorial

Build a Hello World WebPart

Hope this helps !

OTHER TIPS

Steve,

You were so close, you did the StackExchange equivalent of loosening the pickle jar lid for me :-)

You can just add a call to the render method in the webpart's getListData:

// write a value to the web part
private getListData(): void {
    this.properties.lists = `<p>The button worked!</p>`;
    this.render(); // <- add this
}

Also, technically you don't need the _handleClick method -- or you need to call it as your onClick event handler for your button.

I hope this helps?!

Licensed under: CC-BY-SA with attribution
Not affiliated with sharepoint.stackexchange
scroll top