8668020a54
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
74 lines
2.2 KiB
TypeScript
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
|
|
}
|