使用 Next Js Link 时更新 React 类组件
Posted
技术标签:
【中文标题】使用 Next Js Link 时更新 React 类组件【英文标题】:Update a React class component when Next Js Link used 【发布时间】:2021-11-01 20:49:38 【问题描述】:原帖
我有一个用于 Next/JS 动态路径的 React 类组件。在初始加载时一切都很好。但是有一个边缘用例,如果我在已经安装了该组件的页面上,然后单击与组件路径相同的动态基础但具有不同参数的Link
,则该组件不会unmount
和getIntialProps
运行但它不调用构造函数来更新新状态。
路径示例:
动态模式
/vos/[id]/[slug]
初始路径
/vos/203dk2-d33d-3e3e3d/thisName
新路径
/vos/554-34r4f-44d4e/aNewName
活动
1. component loads with initial props/state
2. user clicks Link to same component path but updated params
3. component lifecycles run to reset state but does not and does not unmount
4. 2-5 seconds pass... no activity
5. getIntialProps finally runs with new params
6. componentDidUpdate lifecycle called to update state with new props
我也尝试将Link
更改为Router.push()
,但结果相同。
问题:
-
有没有办法强制卸载组件以允许创建它的新实例。
如果不是上述情况,在组件生命周期中处理这种边缘情况的最佳方法是什么?我尝试使用
componentDidUpdate()
循环中的函数更新状态,但这会有点混乱,因为它在调用 s-s-r
之前运行,因此状态管理不同步。
代码示例
static getInitialProps = async (ctx: NextPageContext) =>
const services = VerifiedOrganizationProfilePage.PageServices(ctx);
const lang: language, id: voId = ctx.query as IProfilePagesQueryParams;
// check VO page is existing
// if VO owner or Admin
let verifiedOrganization: IVerifiedOrganizationResponse | undefined;
let user: IUserResponse | undefined;
let isVoOwner: boolean = false;
let isPlatformAdmin: boolean = false;
let isVoOwnerOrPlatformAdmin: boolean = false;
try
verifiedOrganization = await services.verifiedOrganizationService.getVerifiedOrganization(voId);
if (!verifiedOrganization) throw new Error('No verified organization with that id was found!');
const userId = await services.cognitoIdentityService.getUserIdInSession();
if (userId)
user = await services.usersService.getUser(userId);
isPlatformAdmin = AuthUtil.hasRoles(
[ERole.PLATFORM_ADMIN],
user.platformRoles
);
isVoOwner = OrganizationUtil.isVerifiedOrganizationOwner(
verifiedOrganization.id,
user
);
isVoOwnerOrPlatformAdmin = isVoOwner || isPlatformAdmin;
catch (error)
NextUtil.redirectTo(
'/not-found',
ctx.res,
HTTP_REDIRECT.TEMPORARY,
language
);
// fetch publicly visible data
const store = ctx;
store.dispatch(fetchCampaignsRequest(
verified_organization_id: voId,
limit: isVoOwnerOrPlatformAdmin ? EPaginationLimit.FIVE_HUNDRED : EPaginationLimit.DEFAULT,
, ctx));
store.dispatch(fetchCausesRequest(
verified_organization_id: voId,
limit: EPaginationLimit.DEFAULT
, ctx));
store.dispatch(fetchCommentsRequest(
verified_organization_id: voId,
limit: EPaginationLimit.DEFAULT
, ctx));
store.dispatch(fetchUpdatesRequest(
verified_organization_id: voId,
limit: EPaginationLimit.DEFAULT
, ctx));
// wait for redux saga updating state
await new Promise<void>((resolve) =>
const unsubscribe = store.subscribe(() =>
const state = store.getState();
if (!state.campaign.isFetching && !state.cause.isFetching && !state.comment.isFetching && !state.update.isFetching)
unsubscribe();
resolve();
);
);
return
user,
voId,
isVoOwner,
isPlatformAdmin,
verifiedOrganization,
isVoOwnerOrPlatformAdmin,
tabValue: EVerifiedOrganizationProfilePageTabs.CAMPAIGNS,
pageUrl: NextUtil.getPageUrl(ctx),
;
...
constructor(props)
super(props);
this.state = ...this.props
...
// used to check new props from VO if coming from another VO page and set the state
static async getDerivedStateFromProps(nextProps: IVerifiedOrganizationProfilePageProps, prevState: IVerifiedOrganizationProfilePageState)
if (nextProps.voId !== prevState.voId)
return
voId: nextProps.voId,
urlChanged: true,
tabValue: EVerifiedOrganizationProfilePageTabs.CAMPAIGNS,
isWaitingAdminApproval: false,
isUserBothVoAndIpRepresentative: false,
visibleBeneficiaryList: listResponse,
beneficiaryGroups: listResponse,
followingVerifiedOrganizations: ,
beneficiaryBlockchainCSVData: undefined,
userRating: undefined,
isLoading: true,
;
...
async componentDidMount()
Router.events.on('routeChangeStart', this.handleRouteChangeComplete); // to trigger callback beofre NEXT Router/Link executes
await this.fetchPersonalData(); // method to fetch user specific data
...
async componentDidUpdate()
if (this.state.urlChanged)
await this.fetchPersonalData();
...
componentWillUnmount()
Router.events.off('routeChangeStart', this.handleRouteChangeComplete);
...
// sets the current open tab to CAMPAIGNS if a VO navigates to a connected VO profile from a restricted tab
public handleRouteChangeComplete = async (url: string) =>
this.setState(tabValue: EVerifiedOrganizationProfilePageTabs.CAMPAIGNS,);
...
public fetchPersonalData = async () =>
const voId, user, verifiedOrganization, isPlatformAdmin, isVoOwnerOrPlatformAdmin = this.props;
let isVoRepresentative: boolean = false;
let isIpRepresentative: boolean = false;
let isUserBothVoAndIpRepresentative: boolean = false;
let isWaitingAdminApproval: boolean = false;
let visibleBeneficiaryList: IListResponse<IBeneficiaryWithInvitationStatus> | undefined;
let beneficiaryGroups: IListResponse<IGroup> | undefined;
try
const services = VerifiedOrganizationProfilePage.PageServices();
if (user)
isWaitingAdminApproval = verifiedOrganization.verifiedOrganizationStatus === EVerifiedOrganizationStatus.PENDING_PLATFORM_ADMIN_APPROVAL;
// If Verified Organization is waiting for Admin Platform approval, only Platform Admin can see the page.
if (isWaitingAdminApproval && !isPlatformAdmin)
throw new NotFoundError();
isVoRepresentative = AuthUtil.hasRoles(
[ERole.VERIFIED_ORGANIZATION_REPRESENTATIVE],
user.platformRoles
);
isIpRepresentative = AuthUtil.hasRoles(
[ERole.IMPLEMENTING_PARTNER_REPRESENTATIVE],
user.platformRoles
);
isUserBothVoAndIpRepresentative =
isVoRepresentative && isIpRepresentative;
// If Verified Organization is waiting for Admin Platform approval, only Platform Admin can see the page.
if (isWaitingAdminApproval && !isPlatformAdmin)
throw new NotFoundError();
// add the prefix to the id so we can match the record in the Connections table.
const prefixedId = EIdTypes.VERIFIED_ORGANIZATION.toUpperCase() + '#' + verifiedOrganization.id;
// Fetch data visible only to VoOwner and Aidonic
const connections = [] as unknown as IListResponse<IConnectionVOIP>;
if (isVoOwnerOrPlatformAdmin)
// Get from the API all the connections sent or received
// Commenting this out as it calling twice the API. The call to the API is done from the Tab instead.
// connections = await services.connectionsService.getVisibleConnectionsByOrganization(prefixedId);
visibleBeneficiaryList = await services.beneficiaryService.getBeneficiariesVisibleToOrganization(prefixedId);
beneficiaryGroups = await services.beneficiaryGroupsService.getBeneficiaryGroupsList(prefixedId, limit: EPaginationLimit.THIRTY);
const follows = await services.followsService.getFollowsList(
user_id: user.id
);
const [followingVerifiedOrganizations] = mapFollowsByKey(follows, [
'verifiedOrganizationId'
]);
const userRating = await services.ratingsService.getRatingList(
user_id: user.id,
verified_organization_id: verifiedOrganization.id
);
this.setState(
voId,
connections,
tabValue: EVerifiedOrganizationProfilePageTabs.CAMPAIGNS,
beneficiaryGroups,
isWaitingAdminApproval,
visibleBeneficiaryList,
followingVerifiedOrganizations,
isUserBothVoAndIpRepresentative,
userRating: userRating && userRating[0],
isLoading: false,
urlChanged: false
);
catch (e)
console.log('Error in data fetching on VO profile page: ', e);
更新
我已将道具从状态中分离出来以维持一个真实来源,并使用getDerivedStateFromProps()
来捕捉变化并调用fetchPersonalData()
。一切正常。
唯一的问题是加载新更新的道具/状态所需的时间似乎是初始加载的两倍。想法?
【问题讨论】:
“加载新更新的道具/状态的时间似乎是初始加载的两倍” - 您在运行生产构建时是否遇到相同的行为 (next build && next start
)?
@juliomalves 我是。比首次安装组件时快一点,但仍然明显更长。我认为正在发生的是,当路由更改时,组件从getIntialProps()
(从不unmounts
)“重新加载”,一切都很好。但在getInitialProps()
的回归和comonentDidUpdate()
生命周期之间,存在一个瓶颈。新的 props 被传入,但 state 没有那么快更新。我被难住了。
您能显示getInitialProps
、fetchPersonalData
和handleRouteChangeComplete
的代码吗?有哪些耗时的操作?
@juliomalves 我肯定会把它贴在上面。 getInitialProps()
有几个 redux 调用,但似乎都正常执行。在他们被退回之后,瓶颈发生了。
我想我把范围缩小到fetchPersonalData()
中的两个API调用
【参考方案1】:
解决方案: 就我而言,这是由生命周期中的 API 调用引起的。框架运行良好。
【讨论】:
以上是关于使用 Next Js Link 时更新 React 类组件的主要内容,如果未能解决你的问题,请参考以下文章
在 Next.js 中,如何使用来自 getServerSideProps 的数据更新 React Context 状态?
Next.js:错误:React.Children.only 预期接收单个 React 元素子项
无法在 react/next js 中获取数据,尽管能够控制台记录它