在react中处理输入法合成问题

辅助hook

useImmediateEffect

与useEffect类似 在依赖数组变化后 立刻同步地执行副作用

ts 复制代码
import { useRef } from 'react'

/** 副作用函数 接受旧的依赖项 */
export type ImmediateEffect<Deps extends any[]> = (oldDeps?: Deps) => ImmediateEffectCleanup
/** 清理函数 */
export type ImmediateEffectCleanup = (() => any) | undefined | void

export function useImmediateEffect<Deps extends any[]>(effect: ImmediateEffect<Deps>, deps: Deps) {
  const prevDepsRef = useRef<Deps>()
  const prevCleanupRef = useRef<ImmediateEffectCleanup>()
  if (isDepsChanged(prevDepsRef.current, deps)) {
    prevCleanupRef.current?.()
    prevCleanupRef.current = effect(prevDepsRef.current)
  }
  prevDepsRef.current = deps
}

function isDepsChanged<Deps extends any[]>(prevDeps: Deps | undefined, deps: Deps) {
  if (!prevDeps || prevDeps.length !== deps.length) return true
  return prevDeps.some((prevValue, i) => !Object.is(prevValue, deps[i]))
}

useSemiControlledValue

取得一个值的半受控版本.外界传入的value变化时,此值也会立刻突变;在传入value不变时,此值可以自行变更.

ts 复制代码
import { useRef, useState } from 'react'
import { useMemoizedFn } from 'ahooks'
import { useImmediateEffect } from '../useImmediateEffect'

export function useSemiControlledValue<V = any>(valueController: {
  value?: V
  onChange?: (val: V) => void
}) {
  const { value, onChange } = valueController
  const [changedValue, setChangedValue] = useState(value)
  const currentValueRef = useRef(value)
  useImmediateEffect(() => {
    currentValueRef.current = changedValue
  }, [changedValue])
  useImmediateEffect(() => {
    currentValueRef.current = value
  }, [value])
  const currentValue = currentValueRef.current

  // 对外暴露的更新函数 参数是一个新值或者更新函数
  const onInnerChange: (arg: V | ((val?: V) => V)) => void = useMemoizedFn((arg: any) => {
    const newValue = typeof arg === 'function' ? arg(currentValue) : arg
    setChangedValue(newValue)
    onChange?.(newValue)
  })

  return [currentValue, onInnerChange] as const
}

useComposition

处理输入法合成事件.在本地维护一个输入法无关的value,在合成事件结束后,向外更新合成后的值

ts 复制代码
import { CompositionEventHandler, ChangeEventHandler, useState } from 'react'
import { useMemoizedFn } from 'ahooks'
import { useSemiControlledValue } from '../useSemiControlledValue'

export function useComposition(valueController: {
  value?: string
  onChange: (val: string) => void
}) {
  const [isComposing, setComposing] = useState(false)
  const [value, onInnerChange] = useSemiControlledValue({
    value: valueController.value,
  })
  // onChange时更新本地value
  const onChange = useMemoizedFn<ChangeEventHandler<HTMLInputElement>>((e) => {
    onInnerChange(e.target.value)
  })
  const onCompositionStart = useMemoizedFn(() => {
    setComposing(true)
  })
  // 输入法合成后更新外部value
  const onCompositionEnd = useMemoizedFn<CompositionEventHandler<HTMLInputElement>>((e) => {
    setComposing(false)
    valueController.onChange?.((e.target as HTMLInputElement).value)
  })
  const compositionProps: CompositionProps = {
    value,
    onChange,
    onCompositionStart,
    onCompositionEnd,
  }
  return {
    isComposing,
    compositionProps,
  }
}

使用例

ts 复制代码
const [search,setSearch] = useState<string>()
const { compositionProps } = useComposition({ value: search, onChange: setSearch })

<input {...compositionProps} />
相关推荐
Setsuna_F_Seiei6 小时前
前端转型 Agent 开发 06 之 Agent Memory 记忆系统(让 Agent 更智能,更懂你)
前端·agent·ai编程
zhangzeyuaaa7 小时前
Ruby 多线程、GVL(GIL)与 Mutex 完全指南
开发语言·前端·ruby
默_笙7 小时前
🌊 向量库和 ES 都查不出"关系",我只好给奶茶建了一张人脉网
前端·javascript
优选资讯7 小时前
标签打印软件怎么对接 Excel 数据
前端·excel
一个风轻云淡8 小时前
GCC 和 GDB命令简单解读
java·linux·前端
u0111026759 小时前
图片处理接口如何统一错误信息 前端提示与后端错误码设计
前端·状态模式
福兮说9 小时前
浏览器里把 iPhone 的 HEIC 转成 JPG:Chrome 解不开、转完大了七成、拍摄时间全丢,六个坑实测
前端·javascript·图像处理·chrome·ios·iphone·heic
libokaifa9 小时前
把语音模型塞进手机:sherpa-onnx 在 Android 上的 ASR / TTS
前端
deli00710 小时前
DLA 枝晶生长实测:5000 个粒子集体停摆之后,分形维数收敛到 1.70
前端
IT_陈寒10 小时前
Redis卡顿的锅,这次真不是大key的错
前端·人工智能·后端