42713fbf5f
Making this function a noop on server for bundle size gains with DCE since most of the code is in an useEffect and is not gonna run ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
178 lines
4.2 KiB
TypeScript
178 lines
4.2 KiB
TypeScript
import type { reducer } from './reducer'
|
|
import type {
|
|
ReducerState,
|
|
ReducerAction,
|
|
MutableRefObject,
|
|
Dispatch,
|
|
} from 'react'
|
|
import { useRef, useReducer, useEffect, useCallback } from 'react'
|
|
|
|
function normalizeRouterState(val: any): any {
|
|
if (val instanceof Map) {
|
|
const obj: { [key: string]: any } = {}
|
|
for (const [key, value] of val.entries()) {
|
|
if (typeof value === 'function') {
|
|
obj[key] = 'fn()'
|
|
continue
|
|
}
|
|
if (typeof value === 'object' && value !== null) {
|
|
if (value.$$typeof) {
|
|
obj[key] = value.$$typeof.toString()
|
|
continue
|
|
}
|
|
if (value._bundlerConfig) {
|
|
obj[key] = 'FlightData'
|
|
continue
|
|
}
|
|
}
|
|
obj[key] = normalizeRouterState(value)
|
|
}
|
|
return obj
|
|
}
|
|
|
|
if (typeof val === 'object' && val !== null) {
|
|
const obj: { [key: string]: any } = {}
|
|
for (const key in val) {
|
|
const value = val[key]
|
|
if (typeof value === 'function') {
|
|
obj[key] = 'fn()'
|
|
continue
|
|
}
|
|
if (typeof value === 'object' && value !== null) {
|
|
if (value.$$typeof) {
|
|
obj[key] = value.$$typeof.toString()
|
|
continue
|
|
}
|
|
if (value.hasOwnProperty('_bundlerConfig')) {
|
|
obj[key] = 'FlightData'
|
|
continue
|
|
}
|
|
}
|
|
|
|
obj[key] = normalizeRouterState(value)
|
|
}
|
|
return obj
|
|
}
|
|
|
|
if (Array.isArray(val)) {
|
|
return val.map(normalizeRouterState)
|
|
}
|
|
|
|
return val
|
|
}
|
|
|
|
// Log router state when actions are triggered.
|
|
// function logReducer(fn: typeof reducer) {
|
|
// return (
|
|
// state: ReducerState<typeof reducer>,
|
|
// action: ReducerAction<typeof reducer>
|
|
// ) => {
|
|
// console.groupCollapsed(action.type)
|
|
// console.log('action', action)
|
|
// console.log('old', state)
|
|
// const res = fn(state, action)
|
|
// console.log('new', res)
|
|
// console.groupEnd()
|
|
// return res
|
|
// }
|
|
// }
|
|
|
|
declare global {
|
|
interface Window {
|
|
__REDUX_DEVTOOLS_EXTENSION__: any
|
|
}
|
|
}
|
|
|
|
interface ReduxDevToolsInstance {
|
|
send(action: any, state: any): void
|
|
init(initialState: any): void
|
|
}
|
|
|
|
function devToolReducer(
|
|
fn: typeof reducer,
|
|
ref: MutableRefObject<ReduxDevToolsInstance | undefined>
|
|
) {
|
|
return (
|
|
state: ReducerState<typeof reducer>,
|
|
action: ReducerAction<typeof reducer>
|
|
) => {
|
|
const res = fn(state, action)
|
|
if (ref.current) {
|
|
ref.current.send(action, normalizeRouterState(res))
|
|
}
|
|
return res
|
|
}
|
|
}
|
|
|
|
function useReducerWithReduxDevtoolsNoop(
|
|
fn: typeof reducer,
|
|
initialState: ReturnType<typeof reducer>
|
|
): [
|
|
ReturnType<typeof reducer>,
|
|
Dispatch<ReducerAction<typeof reducer>>,
|
|
() => void
|
|
] {
|
|
const [state, dispatch] = useReducer(fn, initialState)
|
|
|
|
return [state, dispatch, () => {}]
|
|
}
|
|
|
|
function useReducerWithReduxDevtoolsImpl(
|
|
fn: typeof reducer,
|
|
initialState: ReturnType<typeof reducer>
|
|
): [
|
|
ReturnType<typeof reducer>,
|
|
Dispatch<ReducerAction<typeof reducer>>,
|
|
() => void
|
|
] {
|
|
const devtoolsConnectionRef = useRef<ReduxDevToolsInstance>()
|
|
const enabledRef = useRef<boolean>()
|
|
|
|
useEffect(() => {
|
|
if (devtoolsConnectionRef.current || enabledRef.current === false) {
|
|
return
|
|
}
|
|
|
|
if (
|
|
enabledRef.current === undefined &&
|
|
typeof window.__REDUX_DEVTOOLS_EXTENSION__ === 'undefined'
|
|
) {
|
|
enabledRef.current = false
|
|
return
|
|
}
|
|
|
|
devtoolsConnectionRef.current = window.__REDUX_DEVTOOLS_EXTENSION__.connect(
|
|
{
|
|
instanceId: 1,
|
|
name: 'next-router',
|
|
}
|
|
)
|
|
if (devtoolsConnectionRef.current) {
|
|
devtoolsConnectionRef.current.init(normalizeRouterState(initialState))
|
|
}
|
|
|
|
return () => {
|
|
devtoolsConnectionRef.current = undefined
|
|
}
|
|
}, [initialState])
|
|
|
|
const [state, dispatch] = useReducer(
|
|
devToolReducer(/* logReducer( */ fn /*)*/, devtoolsConnectionRef),
|
|
initialState
|
|
)
|
|
|
|
const sync = useCallback(() => {
|
|
if (devtoolsConnectionRef.current) {
|
|
devtoolsConnectionRef.current.send(
|
|
{ type: 'RENDER_SYNC' },
|
|
normalizeRouterState(state)
|
|
)
|
|
}
|
|
}, [state])
|
|
return [state, dispatch, sync]
|
|
}
|
|
|
|
export const useReducerWithReduxDevtools =
|
|
typeof window !== 'undefined'
|
|
? useReducerWithReduxDevtoolsImpl
|
|
: useReducerWithReduxDevtoolsNoop
|