Pnpjs: SPFX React WebPart - this.context.pageContext.web.absoluteUrl is Undefined in Render

Created on 21 Feb 2019  路  7Comments  路  Source: pnp/pnpjs

General Information

  • [ ] Usage
  • [x] Development
  • [ ] Documentation
  • [ ] Feature Request

What Version you are running?

  • NodeJS: V8.15
  • NPM: 6.8.0
  • @pnp/spfx: V1.7.1

Problem Description

Create a HelloWorldWebPart with React Framework
On the Render Method try to retrieve the
this.context.pageContext.web.absoluteUrl

  public render(): React.ReactElement<IHelloWorldProps> {
    return (
      <div className={ styles.helloWorld }>
        <div className={ styles.container }>
          <div className={ styles.row }>
            <div className={ styles.column }>
              <span className={ styles.title }>Welcome to {this.context.pageContext.web.absoluteUrl}</span>
              <p className={ styles.subTitle }>Customize SharePoint experiences using Web Parts.</p>
              <p className={ styles.description }>{escape(this.props.description)}</p>
              <a href="https://aka.ms/spfx" className={ styles.button }>
                <span className={ styles.label }>Learn more</span>
              </a>
            </div>
          </div>
        </div>
      </div>
    );
  }

My goal is to query a SharePoint List using "@pnp/sp"
import { sp } from "@pnp/sp"; ... sp.setup({ spfxContext: this.context });

More Information

the this.context.pageContext.web.absoluteUrl is well defined if I choose "No Framework" during the webpart generation wizard.

So my question is how do I initialize the SharePoint context before using @pnp/sp in React WebPart ?

Any help would be appreciated.

thanks

non-library details needed question

Most helpful comment

@philippe-entringer I see what you did, but I wouldnt suggest that.
That baseurl is mostly for usage outside of SharePoint FrameWork.

  1. Yes you are passing in the values from the context to your react component as a Property. That's a good way to do it.

  2. Don't do that :)
    In the same WebPartName.ts as you just did the render() add a OnInit and do the setup there. Because if you think about it this is where you have the context available, because it's the web parts context, not React context.

// https://pnp.github.io/pnpjs/documentation/getting-started/#using-pnpsp-setup
public onInit(): Promise < void> {
  return super.onInit().then(_ => {
    sp.setup({
      spfxContext: this.context
    });
  });
}
  1. So then it would look like this in your WebPartName.ts:

public onInit(): Promise < void> {
  return super.onInit().then(_ => {
    sp.setup({
      spfxContext: this.context
    });
  });
}

public render(): void {
  const element: React.ReactElement < IDropDownWebPartProps > = React.createElement(
    DropDownWebPart,
    {
      siteUrl: this.context.pageContext.web.absoluteUrl,
    }
}

All 7 comments

@philippe-entringer First this is the wrong repo for pnp/pnpjs specific questions. Questions like this should be posted on pnpjs

I am also not sure where you called:

sp.setup({ spfxContext: this.context });

It should be called somewhere in the render or onInit method according to the documentation

Hi @philippe-entringer - @StfBauer wanted this transferred here. Can you share a link to a minimal reproduction repo? Thanks!

Hi @philippe-entringer!

First off this has nothing to do with PnPJs. When you give the context to PnPJs it's just for PnPJs setup, it has nothing to do where your context is available.
And the context is the Web Parts context.

Your issue is that you are trying to use the context inside your react component, where the web parts context is not available unless you provide it.

I would suggest that you pass in values from the context to your react component and then use them there.

You could see here what I've done in an field customizer: link

And as @StfBauer said the setup should be in the OnInit, dont mind that mistake in my code 馃榾

Let me know if you need some more help with the code

Thanks for the follow up,
I finally found a way to intialize the current sharepoint context with my React Component.

Here are my conclusions regarding the use of "@pnp/sp" in React Component.

1. Init the sharepoint current site url

In the Render method of BaseClientSideWebPart

The only way to Initialize the SharePoint Context is :

public render(): void {
    const element: React.ReactElement<IDropDownWebPartProps > = React.createElement(
      DropDownWebPart,
      {
siteUrl: this.context.pageContext.web.absoluteUrl,
}

2. Setup the sp context in the React WebPart code (*.tsx)

import { sp } from "@pnp/sp";

constructor(props: IDropDownWebPartProps) {
    super(props);
     //https://pnp.github.io/pnpjs/documentation/getting-started/
    sp.setup({
      sp: {
        baseUrl: this.props.siteUrl
      },
    });
  }

private _loadListItems(
{
// load your list items with the sp.setup context
sp.web.lists.getByTitle(thelistName).items
}

this method doesn't work in React

sp.setup({
    spfxContext: this.context
 });

Reference
https://github.com/SharePoint/sp-dev-docs/blob/master/docs/spfx/web-parts/guidance/connect-to-sharepoint-using-jsom.md

@philippe-entringer I see what you did, but I wouldnt suggest that.
That baseurl is mostly for usage outside of SharePoint FrameWork.

  1. Yes you are passing in the values from the context to your react component as a Property. That's a good way to do it.

  2. Don't do that :)
    In the same WebPartName.ts as you just did the render() add a OnInit and do the setup there. Because if you think about it this is where you have the context available, because it's the web parts context, not React context.

// https://pnp.github.io/pnpjs/documentation/getting-started/#using-pnpsp-setup
public onInit(): Promise < void> {
  return super.onInit().then(_ => {
    sp.setup({
      spfxContext: this.context
    });
  });
}
  1. So then it would look like this in your WebPartName.ts:

public onInit(): Promise < void> {
  return super.onInit().then(_ => {
    sp.setup({
      spfxContext: this.context
    });
  });
}

public render(): void {
  const element: React.ReactElement < IDropDownWebPartProps > = React.createElement(
    DropDownWebPart,
    {
      siteUrl: this.context.pageContext.web.absoluteUrl,
    }
}

All is cristal clear now.
Thanks Simon !

Thanks, going to close this as answered.

Was this page helpful?
0 / 5 - 0 ratings