正如标题所示,我正在尝试使用Promise设置我的react组件的状态。很简单,对不对?也许不吧。当我尝试在promise中进行后续的ajax调用时,就会出现问题。
代码在下面。如您所见,我正在尝试映射返回的数据并创建一个新对象。在创建该对象的过程中,还有另一个promise调用,应该设置其中一个字段。所有这些都可以正常工作,但是在第二个承诺完成之前就已经设置了状态,并且该字段对我不可用。
我尝试使用async / await使其等待呼叫完成,但是这种方法我也没有成功。
任何建议,将不胜感激。
我正在ComponentDidMount方法中进行呼叫:
public componentDidMount(): void {
this._spApiService
.getListItems(this.props.context, "Company News")
.then((spNewsStories: SpNewsStory[]) => {
return spNewsStories.map((newsStory: SpNewsStory) => {
return new AdwNewsStory(newsStory, this.props.context);
});
})
.then((adwNewsStories: AdwNewsStory[]) => {
this.setState({
topStories: adwNewsStories,
});
});
}
这是AdwNewStory类,它进行了第二个ajax调用:
import { SpNewsStory } from "./SpNewsStory";
import { ISpApiService } from "../../interfaces/ISpApiService";
import SpApiService from "../../services/SpApiService";
import { WebPartContext } from "../../../node_modules/@microsoft/sp-webpart-
base";
import { SpAttachment } from "../SpAttachment";
import IEnvironmentService from "../../interfaces/IEnvironmentService";
import EnvironmentService from "../../services/EnvironmentService";
import { IAdwDateTimeService } from "../../interfaces/IAdwDateTimeService";
import AdwDateTimeService from "../../services/AdwDateTimeService";
class AdwNewsStory {
public id: number;
public title: string;
public publishDate: string;
public storySummary: string;
public storyLink: string;
public windowTarget: string;
public imageUrl: string;
public imageAlternativeText: string;
public attachments: boolean;
private _spApiService: ISpApiService;
private _context: WebPartContext;
private _environmentService: IEnvironmentService;
private _adwDateTimeService: IAdwDateTimeService;
constructor(spNewsStory: SpNewsStory, context?: WebPartContext) {
this._spApiService = new SpApiService();
this._context = context;
this._environmentService = new EnvironmentService();
this._adwDateTimeService = new AdwDateTimeService();
this.buildAdwNewsStory(spNewsStory);
}
private buildAdwNewsStory = (spNewsStory: SpNewsStory): void => {
this.id = spNewsStory.Id;
this.title = spNewsStory.Title;
this.publishDate = this.setPublishDate(spNewsStory.PublishDate);
this.storySummary = spNewsStory.StorySummary;
this.storyLink = spNewsStory.Link.Description;
this.windowTarget = spNewsStory.WindowTarget;
this.imageAlternativeText = spNewsStory.ImageAlternateText;
this.attachments = spNewsStory.Attachments;
if (this.attachments) {
this.setImageUrl();
}
};
private setImageUrl = (): void => {
this._spApiService.getListItemAttachments(this._context, "Company News", this.id).then(attachments => {
const siteUrl: string = this._environmentService.getSiteUrl();
const attchmentUrl: string = `${siteUrl}/Lists/Company%20News/Attachments/${this.id}/${attachments[0].FileName}`;
this.imageUrl = attchmentUrl;
});
};
private setPublishDate = (dateString: string) => {
const publishDate: Date = new Date(dateString);
return `${this._adwDateTimeService.getMonthName(publishDate.getMonth())} ${publishDate.getDate()}, ${publishDate.getFullYear()}`;
};
}
export default AdwNewsStory;
答案 0 :(得分:1)
您在这里有几个问题。首先:
private setImageUrl = (): void => {
// You aren't returning this promise here, so there's no way for
// calling code to get notified when the promise resolves
this._spApiService.getListItemAttachments(this._context, "Company News", this.id).then(attachments => {
const siteUrl: string = this._environmentService.getSiteUrl();
const attchmentUrl: string = `${siteUrl}/Lists/Company%20News/Attachments/${this.id}/${attachments[0].FileName}`;
this.imageUrl = attchmentUrl;
});
};
但是,更大的问题是,您正在从构造函数中调用该方法。您确实不希望构造函数进行异步调用。虽然没有技术理由,但实际上没有办法将承诺兑现给呼叫者。这会导致您遇到的错误。
我建议将buildAdwNewsStory()
方法移出构造函数,并分别调用它,因为它涉及异步调用。
class AdwNewsStory {
// ... fields...
constructor(context?: WebPartContext) {
this._spApiService = new SpApiService();
this._context = context;
this._environmentService = new EnvironmentService();
this._adwDateTimeService = new AdwDateTimeService();
}
public buildAdwNewsStory(spNewsStory: SpNewsStory): Promise<void> {
this.id = spNewsStory.Id;
this.title = spNewsStory.Title;
this.publishDate = this.setPublishDate(spNewsStory.PublishDate);
this.storySummary = spNewsStory.StorySummary;
this.storyLink = spNewsStory.Link.Description;
this.windowTarget = spNewsStory.WindowTarget;
this.imageAlternativeText = spNewsStory.ImageAlternateText;
this.attachments = spNewsStory.Attachments;
if (this.attachments) {
return this.setImageUrl();
} else {
return Promise.resolve();
}
}
...
private setImageUrl() {
return this._spApiService.getListItemAttachments(this._context, "Company News", this.id)
.then(attachments => {
const siteUrl: string = this._environmentService.getSiteUrl();
const attchmentUrl: string = `${siteUrl}/Lists/Company%20News/Attachments/${this.id}/${attachments[0].FileName}`;
this.imageUrl = attchmentUrl;
});
}
}
然后从您的componentDidMount
:
public componentDidMount(): void {
this._spApiService
.getListItems(this.props.context, "Company News")
.then((spNewsStories: SpNewsStory[]) =>
spNewsStories.map((newsStory: SpNewsStory) => {
var story = new AdwNewsStory(this.props.context);
return story.buildAdwNewsStory(newsStory);
})
)
.then((adwNewsStories: AdwNewsStory[]) => {
this.setState({
topStories: adwNewsStories,
});
});
}
请注意,如果您移至async/await
而不是直接使用Promise
方法(.then
/ .catch
),则此代码看起来可能更简洁。
答案 1 :(得分:0)
在这种情况下,setState会在您的诺言得到解决之前触发,因为尽管调用了异步操作,您的类的构造函数仍会立即返回。
您可以重新考虑设置,并在创建实例时向AdwNewStory
实例提供所需的信息,以便更好地控制操作流程,也可以将回调传递给{{1} },并允许它告诉您创建故事的时间,但这会导致您遇到反模式。