Based on the error you posted, this seems like a timing issue, possibly compounded by trying to implement a function/event from one framework's lifecycle in a different framework.
First, the timing issue. Clearly the error you are getting that cannot read property get of undefined
refers to your line of code where you try calling this.ordersClient.get()
. That's most likely happening because
componentDidMount() {
this.callThis(); // <-- 1. first this is called, then...
}
public callThis(){
this.ordersClient.get() // <-- 2. this is called...
.then((res: HttpClientResponse): Promise<any> => {
return res.json();
}), (err: any): void => {
console.log(err);
};
}
protected onInit(): Promise<void> {
return new Promise<void>((resolve: () => void, reject: (error: any) => void): void => {
this.context.aadHttpClientFactory
.getClient('c3e425a8-e8b4-408d-8370-43b9abbb23c4')
.then((client: AadHttpClient): void => {
this.ordersClient = client; // <-- 3. BEFORE this happens
resolve();
}, err => reject(err));
});
}
I have no idea whether the initial firing of the onInit()
function happens before or after the initial firing of componentDidMount()
, the main point to consider is that this.context.aadHttpClientFactory.getClient()
is asynchronous, and no matter when the initial call goes out, clearly the code that gets to this.ordersClient.get()
is trying to call get
on an AadHttpClient
that isn't there yet.
That does bring us to the second idea though -- when I say
I have no idea whether the initial firing of the onInit()
function
happens before or after the initial firing of componentDidMount()
the other thing to consider there is that you pulled your example of using onInit()
from that article, where the onInit()
function is implemented inside a class that extends BaseClientSideWebPart
. From the article you linked to:
export default class OrdersWebPart extends BaseClientSideWebPart<IOrdersWebPartProps> {
private ordersClient: AadHttpClient;
protected onInit(): Promise<void> {
// shortened for brevity
}
}
However, in your actual code, you are trying to implement onInit()
inside a class that extends React.Component
. From your code:
export default class SecureFinalAd extends React.Component<ISecureFinalAdProps, {}> {
public ordersClient: AadHttpClient;
protected onInit(): Promise<void> {
// shortened for brevity
}
public callThis() {
// shortened for brevity
}
componentDidMount() {
// shortened for brevity
}
}
What I would assume is that onInit()
is a function that is called as part of the SharePoint Framework lifecycle, and so therefore when you try to implement it inside a class that extends React.Component
, it's actually just like defining a regular function, and since onInit()
is not part of the React lifeceycle, your onInit
function is never even getting called, which compounds the timing issues mentioned above, because if your onInit
is never called, then the async call to aadHttpClientFactory.getClient()
never happens, so of course it never even has a chance to complete and retrieve the client object.
What you could try, since your component is extending React.Component
and you are tied in to the React lifecycle events, is abandoning onInit
completely and just start the whole process of getting your AadHttpClient
from componentDidMount
. Something along the lines of:
export default class SecureFinalAd extends React.Component<ISecureFinalAdProps, {}> {
public aadClient: AadHttpClient;
public myFunctionResponse: any;
componentDidMount() {
// get the client object
this.context.aadHttpClientFactory
.getClient('c3e425a8-e8b4-408d-8370-43b9abbb23c4')
.then((client: AadHttpClient): void => {
// now that you have the client,
// save it so you can access it
// later if you need to
this.aadClient = client;
// BUT - you still want to call your
// Azure function as soon as you have
// access to the client, so go ahead
// and do that
this.aadClient
.get('https://spfx-testing.azurewebsites.net/api/spfx-testing', AadHttpClient.configurations.v1)
.then((res: HttpClientResponse): void => {
// HttpClientResponse.json() is an async
// function in and of itself, so you need to
// treat is as such in order to get to the _actual_
// response content from your Azure function
res.json().then((azureFunctionJson: any): void => {
// do something with the actual response content
console.log(azureFunctionJson);
this.myFunctionResponse = azureFunctionJson;
});
}), (err: any): void => {
console.log(err);
};
}, (err: any): void => {
console.log(err);
});
}
}
(Note: because the original example you linked to used an instance property, which might be appropriate for something that extends the SPFx class BaseClientSideWebPart
, and you used the same pattern in your class, I also used that for the additional property storing the response, however the question of when to use instance properties vs. state in React is a whole other can of worms.)