rsnext/packages/next/shared/lib/side-effect.tsx
Jiachi Liu 8668020a54
Upgrade typescript to 4.8.2 (#39979)
Typescript published 4.8.2 today and it fails CI, bump our typescript version to 4.8.2 and tweak some typings to make existing e2e typescript tests work properly

* Bump web-vitals from 3.0.0-beta to 3.0.0 stable for typing fix (there's an undefined type but it wasn't caught by ts 4.7), also force compiled it as CJS for pre-compiled
* Bump ncc to 3.34.0 for ts-loader compatibility for new typescript version, ncc 3.33.x cannot work with ts 4.8
* Update pre-compiled
2022-08-29 16:56:02 +00:00

74 lines
2.2 KiB
TypeScript

import React, { Children, useEffect, useLayoutEffect } from 'react'
type State = JSX.Element[] | undefined
export type SideEffectProps = {
reduceComponentsToState: <T extends {}>(
components: Array<React.ReactElement<any>>,
props: T
) => State
handleStateChange?: (state: State) => void
headManager: any
inAmpMode?: boolean
children: React.ReactNode
}
const isServer = typeof window === 'undefined'
const useClientOnlyLayoutEffect = isServer ? () => {} : useLayoutEffect
const useClientOnlyEffect = isServer ? () => {} : useEffect
export default function SideEffect(props: SideEffectProps) {
const { headManager, reduceComponentsToState } = props
function emitChange() {
if (headManager && headManager.mountedInstances) {
const headElements = Children.toArray(
Array.from(headManager.mountedInstances as Set<unknown>).filter(Boolean)
) as React.ReactElement[]
headManager.updateHead(reduceComponentsToState(headElements, props))
}
}
if (isServer) {
headManager?.mountedInstances?.add(props.children)
emitChange()
}
useClientOnlyLayoutEffect(() => {
headManager?.mountedInstances?.add(props.children)
return () => {
headManager?.mountedInstances?.delete(props.children)
}
})
// We need to call `updateHead` method whenever the `SideEffect` is trigger in all
// life-cycles: mount, update, unmount. However, if there are multiple `SideEffect`s
// being rendered, we only trigger the method from the last one.
// This is ensured by keeping the last unflushed `updateHead` in the `_pendingUpdate`
// singleton in the layout effect pass, and actually trigger it in the effect pass.
useClientOnlyLayoutEffect(() => {
if (headManager) {
headManager._pendingUpdate = emitChange
}
return () => {
if (headManager) {
headManager._pendingUpdate = emitChange
}
}
})
useClientOnlyEffect(() => {
if (headManager && headManager._pendingUpdate) {
headManager._pendingUpdate()
headManager._pendingUpdate = null
}
return () => {
if (headManager && headManager._pendingUpdate) {
headManager._pendingUpdate()
headManager._pendingUpdate = null
}
}
})
return null
}