UNPKG

@ohkit/react-helper

Version:

some utils and hooks for react

1 lines 6.86 kB
{"version":3,"file":"index.modern.mjs","sources":["../src/assign-ref.ts","../src/hooks/runtime.ts","../src/hooks/compatible-effect.ts","../src/hooks/sync-props-state/index.ts"],"sourcesContent":["/**\n * @file react ref 工具函数\n */\n\nexport function assignRef<T>(registerRef: React.RefCallback<T> | React.RefObject<T>, ref: T | null) {\n if (typeof registerRef === 'function') {\n registerRef(ref);\n } else if (registerRef && registerRef.hasOwnProperty('current')) {\n // registerRef.current = ref;\n Object.assign(registerRef, { current: ref });\n }\n}\n","import {useRef} from 'react';\nimport {pick} from 'lodash-es';\n\n/**\n * P 可以是 T 和 U 中的属性,P是U中属性时类型必须匹配 U[P]\n */\ntype PartialOfMixin<T, U> = {\n [P in keyof U | keyof T]?: P extends keyof U ? U[P] : unknown;\n};\n\nexport type AssignRuntime<U> = <T extends PartialOfMixin<T, U>>(props: T) => void;\n\n/**\n * 用于function组件挂载一些字段缓存(类似class组件的this上挂载)\n * @param {T} init extends Record<PropertyKey, unknown>\n * @param {Array<keyof T>} [syncKeys] 每次render时需要同步的字段\n * @return {*} [T, <U extends PartialOfMixin<T, U>>(props: U) => void]\n */\nexport function useRuntime<T extends object>(\n init: T,\n syncKeys?: Array<keyof T>\n): [T, AssignRuntime<T>] {\n const runtimeRef = useRef(init);\n const assignRuntime = (props = {}) => {\n if (props && typeof props === 'object') {\n Object.assign(runtimeRef.current, props);\n }\n };\n if (syncKeys) {\n assignRuntime(pick(init, syncKeys));\n }\n return [runtimeRef.current, assignRuntime];\n}\n\n","import {useEffect, useLayoutEffect} from 'react';\nimport {inBrowser} from '@ohkit/platform';\n\n/**\n * 使用兼容的副作用钩子函数\n *\n * @param effect 要执行的副作用函数 浏览器端使用 useLayoutEffect,ssr 使用 useEffect\n * @param deps 副作用函数的依赖列表,当依赖变化时,会重新执行副作用函数\n * @returns 无返回值\n */\nexport const useCompatibleEffect = (effect: React.EffectCallback, deps?: React.DependencyList) => {\n if (inBrowser) {\n useLayoutEffect(effect, deps);\n } else {\n useEffect(effect, deps);\n }\n};\n","import {useState, useCallback} from 'react';\nimport {useRuntime} from '../runtime';\nimport {useCompatibleEffect} from '../compatible-effect';\n\nexport interface ISyncPropsStateOptions<T, K extends keyof T, Val extends T[K] = T[K]> {\n defaultValue?: Val;\n onChange?: (val: Val extends undefined ? T[K] : Required<T>[K]) => void;\n // layoutEffect?: boolean;\n /**\n * 自定义比较函数\n * @param pre 前一个值\n * @param cur 当前值\n */\n compare?: (pre: T[K], cur: T[K]) => boolean;\n}\n\n/**\n * 使用组件的 props 属性同步组件状态\n *\n * @description T[K] 不能是个函数类型,在 React 中,useState 通常用于管理非函数类型的状态(如数字、字符串、对象等)\n * 如果你需要管理一个函数类型的状态,直接使用 useState 可能会导致一些问题,因为 React 会将函数视为惰性初始化的函数,而不是状态值本身。\n *\n * @param key 要同步的 props 的键名\n * @param props 组件的 props 对象\n * @param options 可选配置参数\n * @returns 返回内部状态和更新状态的函数\n */\nexport function useSyncPropsState<T extends object, K extends keyof T, Val extends T[K] = T[K]>(\n props: T,\n key: K,\n options?: ISyncPropsStateOptions<T, K, Val>\n): [Val extends undefined ? T[K] : Required<T>[K], (val: T[K] extends undefined ? T[K] : Required<T>[K], emitChange?: boolean) => void] {\n const propVal = props[key];\n const isFuncState = typeof propVal === 'function';\n // const isObjectState = typeof propVal === 'object';\n const {\n onChange,\n // layoutEffect = false,\n defaultValue = propVal,\n compare = (pre: T[K], cur: T[K]) => pre === cur,\n } = options || {};\n\n // 考虑可能有 function 类型的 state 惰性初始化\n const [inner, setInner] = useState(() => typeof propVal !== 'undefined' ? propVal : defaultValue);\n\n const [runtime] = useRuntime({\n onChange,\n compare,\n inner,\n isFuncState,\n }, ['compare', 'onChange', 'inner', 'isFuncState']);\n const setFinalInner = useCallback((val: typeof propVal, emitChange = true) => {\n if (!runtime.compare?.(runtime.inner, val)) {\n // console.log('[useSyncPropsState] real change pre => cur: ', runtime.inner, val);\n // function 类型的state 需要惰性更新\n setInner(runtime.isFuncState ? () => val : val);\n // init 时,不应触发change\n emitChange && runtime.onChange?.(val);\n }\n }, [runtime]);\n\n // const effect = layoutEffect ? useLayoutEffect : useEffect;\n useCompatibleEffect(() => {\n // console.log('[useSyncPropsState] propVal useEffect change: ', propVal);\n let newVal = propVal;\n if (\n typeof propVal === 'undefined'\n && typeof defaultValue !== 'undefined'\n ) {\n newVal = defaultValue;\n }\n setFinalInner(newVal, false);\n }, [defaultValue, propVal]);\n\n return [inner, setFinalInner];\n}\n"],"names":["assignRef","registerRef","ref","hasOwnProperty","Object","assign","current","useRuntime","init","syncKeys","runtimeRef","useRef","assignRuntime","props","pick","useCompatibleEffect","effect","deps","inBrowser","useLayoutEffect","useEffect","useSyncPropsState","key","options","propVal","isFuncState","onChange","defaultValue","compare","pre","cur","inner","setInner","useState","runtime","setFinalInner","useCallback","val","emitChange","newVal"],"mappings":"+KAIgB,SAAAA,EAAaC,EAAwDC,GACtD,mBAAhBD,EACPA,EAAYC,GACLD,GAAeA,EAAYE,eAAe,YAEjDC,OAAOC,OAAOJ,EAAa,CAAEK,QAASJ,GAE9C,UCOgBK,EACZC,EACAC,GAEA,MAAMC,EAAaC,EAAOH,GACpBI,EAAgBA,CAACC,EAAQ,MACvBA,GAA0B,iBAAVA,GAChBT,OAAOC,OAAOK,EAAWJ,QAASO,IAM1C,OAHIJ,GACAG,EAAcE,EAAKN,EAAMC,IAEtB,CAACC,EAAWJ,QAASM,EAChC,CCtBa,MAAAG,EAAsBA,CAACC,EAA8BC,KAC1DC,EACAC,EAAgBH,EAAQC,GAExBG,EAAUJ,EAAQC,ICaV,SAAAI,EACZR,EACAS,EACAC,GAEA,MAAMC,EAAUX,EAAMS,GAChBG,EAAiC,mBAAZD,GAErBE,SACFA,EAAQC,aAERA,EAAeH,EAAOI,QACtBA,EAAUA,CAACC,EAAWC,IAAcD,IAAQC,GAC5CP,GAAW,CAAA,GAGRQ,EAAOC,GAAYC,EAAS,SAAyB,IAAZT,EAA0BA,EAAUG,IAE7EO,GAAW3B,EAAW,CACzBmB,WACAE,UACAG,QACAN,eACD,CAAC,UAAW,WAAY,QAAS,gBAC9BU,EAAgBC,EAAY,CAACC,EAAqBC,GAAa,KAC5DJ,MAAAA,EAAQN,SAARM,EAAQN,QAAUM,EAAQH,MAAOM,KAGlCL,EAASE,EAAQT,YAAc,IAAMY,EAAMA,GAE3CC,IAAcJ,MAAAA,EAAQR,UAARQ,EAAQR,SAAWW,MAEtC,CAACH,IAeJ,OAZAnB,EAAoB,KAEhB,IAAIwB,EAASf,OAEU,IAAZA,QACoB,IAAjBG,IAEVY,EAASZ,GAEbQ,EAAcI,GAAQ,IACvB,CAACZ,EAAcH,IAEX,CAACO,EAAOI,EACnB"}