//为什么逻辑感觉都走完了,还会打印出来一遍40呢,又返回之前的地方
image.png还有就时同一个hook,根据日志返现其实被调用了两次,由于使用这个hook,会导致主函数也会render两次。这样做会不会浪费啊
hook最麻烦的就是不确定自己写的函数到底执行了多少次,虽然看起来效果是对的,但是打印log的时候,一些自己感觉不会再执行到的地方也会打印出东西来
关键,组合键的实现不支持任意键,只是支持和快捷键的组合
import { MutableRefObject } from 'react';
// function useRef<T>(initialValue: T): MutableRefObject<T>;
// interface MutableRefObject<T> {
// current: T;
// }
export type BasicTarget<T = HTMLElement> =
| (() => T | null)
| T
| null
| MutableRefObject<T | undefined>;
type TargetElement =HTMLElement|Document|Window
export function getTargetElement(
target?:BasicTarget<TargetElement>,
defaultElemnt?:TargetElement
):TargetElement|undefined|null{
if(!target){
return defaultElemnt
}
let targetElement:TargetElement|undefined|null
if(typeof target ==='function'){
targetElement=target()
}else if('current' in target){
targetElement=target.current
}else{
targetElement=target
}
return targetElement
}
export function isType(obj:any){
return Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/, '$1').toLowerCase()
}
import {BasicTarget,getTargetElement,isType} from '../src/utils/dom'
import {useEffect,useCallback,useRef}from 'react'
export type keyPredicate=(event:KeyboardEvent)=>boolean
export type KeyType=KeyboardEvent['keyCode']|KeyboardEvent['key']
export type KeyFilter=KeyType|Array<KeyType>|((event:KeyboardEvent)=>boolean)
export type EventHandler=(event:KeyboardEvent)=>void
export type KeyEvent='keydown'|'keyup'
export type Target=BasicTarget<HTMLElement|Document|Window>
const defaultEvents:Array<KeyEvent>=["keydown"]
export type EventOption={
events?:Array<KeyEvent>
target?:Target
}
const keyCodeMap:any={
esc:27,
tab:9,
enter:13,
space:32,
up:38,
left:37,
right:39,
down:40,
delete:[8,46]
}
const keyMap:any={
esc:"Escape",
tab:"Tab",
enter:'Enter',
sapce:" ",
up:["Up","ArrowUp"],
left:["Left","ArrwowLeft"],
right:["Right","ArrowRight"],
down:["Down","ArrowDown"],
delete:["backspace","Delete"]
}
const modifierKey:any={
ctrl:(event:KeyboardEvent)=>event.ctrlKey,
shift:(event:KeyboardEvent)=>event.shiftKey,
alt:(event:KeyboardEvent)=>event.altKey,
meta:(event:KeyboardEvent)=>event.metaKey,
// 他只支持快捷键的串联组合,并不支持任意键的组合
}
const noop=()=>{}
// 返回空对象
// 输入检测函数
function getKeyFormater(keyFilter:any):keyPredicate{
const type=isType(keyFilter)
// 需要检测的按键类型
if(type=='function'){
return keyFilter
}
if(type==='string'||type==='number'){
return (event:KeyboardEvent)=>genFilterKey(event,keyFilter)
}
if(type==='array'){
return (event:KeyboardEvent)=>keyFilter.some((item:any)=>genFilterKey(event,item))
}
return keyFilter?()=>true:()=>false
}
// 检测当前按的键是否和定义的一致
function genFilterKey(event:KeyboardEvent,keyFilter:any):boolean{
let genLen=0
console.log("当前需要检测的键位是------------------>",keyFilter)
const type=isType(keyFilter)
if(type==='number'){
return event.keyCode===keyFilter
// 如果需要判断的是数字码,那就直接和按键的码对比
}
const genArr=keyFilter.split('.')
// 如果是字符串的话,要判断是否有组合键
for(let key in genArr){
const genModifier=modifierKey[genArr[key]]
// 组合键
const kM=keyMap[genArr[key]]
// key别名
const kCM=keyCodeMap[genArr[key]]
// console.log(event.key.toUpperCase(),"输入后面有快捷键",genModifier)
// console.log(genArr[key].toUpperCase(),"对比")
// console.log(event.key.toUpperCase()==genArr[key].toUpperCase(),"是否相等")
if(genModifier&&genModifier(event)||
// 判断是否有快捷键
(kCM&&isType(kCM)==='array'?kCM.includes(event.keyCode):kCM==event.keyCode)||
// 判断自定义别名
(kM&&isType(kM)==='array'?kM.includes(event.key):kM==event.key)||
event.key.toUpperCase()==genArr[key].toUpperCase()
// 判断是否有普通按键
){
genLen++
}
}
return genLen==genArr.length
}
export default function useKeyPress(
keyFilter:KeyFilter,
eventHandler:EventHandler=noop,
option:EventOption={}
){
console.log('useKeypress')
const {events=defaultEvents,target}=option
const callbackRef=useRef(eventHandler)
callbackRef.current=eventHandler
const callbackHandler=useCallback(
(event)=>{
const genGuard:keyPredicate=getKeyFormater(keyFilter)
console.log(genGuard)
if(genGuard(event)){
console.log(true)
return callbackRef.current(event)
}else{
console.log("这个没有检测到哦")
}
},
[keyFilter]
)
useEffect(()=>{
console.log(target)
const el=getTargetElement(target,window)!;
console.log(el)
// 这里加感叹号是什么意思?
// 非空断言运算符:ts编译器无法自动推断时断言表达式不为null或者undefined
for(let key in events){
el.addEventListener(events[key],callbackHandler)
}
return ()=>{
for(let key in events){
el.removeEventListener(events[key],callbackHandler)
}
}
},[events,callbackHandler,typeof target==='function'?undefined:target])
}
const [inputValue,setInputValue]=useState('default')
// 这里输入的类型要怎么限制呢
const [count,setCount]=useState(0)
// useTitle(inputValue)
// 设置标题:现在每次重新刷新都会调用useTitle,好像连初始的时候都会调用两次,为什么一次渲染会调到两次useTitle
function handleChange(e: { target: { value: React.SetStateAction<string>; }; }):void{
setInputValue(e.target.value)
}
// 主动修改
function handleClick():void{
setCount(c=>c+1)
}
// useKeyPress
// useKeyPress("ArrowUp",()=>{
// console.log('ArrowUp')
// })
// useKeyPress([40,38],()=>{
// console.log('keyCode:40 or 38')
// })
// 使用别名
// useKeyPress("left",()=>{
// console.log('left')
// })
// useKeyPress("up",()=>{
// console.log('left')
// })
// useKeyPress("delete",()=>{
// console.log('left')
// })
// 组合键
// 组合键只支持修饰键+键位别名/键盘事件中的key进行组合
// useKeyPress(['shift.c'], () => {
// });
// useKeyPress(['ctrl.alt.shift'], () => {
// });
// useKeyPress(['ctrl.up'], () => {
// });
// 传入一个返回布尔值的回调函数,处理一些预处理的操作
// const [key, setKey] = useState<string>();
// const filterKey = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
// useKeyPress(
// (event) => !filterKey.includes(event.key),
// (event) => {
// if (event.type === 'keyup') {
// setKey(event.key);
// }
// },
// {
// events: ['keydown', 'keyup'],
// },
// );
// 支持传入dom
// useKeyPress(
// 'enter',
// // 按下enter触发检测
// (event: any) => {
// const { value } = event.target;
// console.log('ok',value)
// },
// // 符合检测条件触发的函数
// {
// target: () => document.getElementById('input'),
// },
// // 需要绑定的目标
// );
// const inputRef = useRef()as React.MutableRefObject<HTMLInputElement>
//注意定义ref的typeScrit形式
// useKeyPress(
// ()=>true,
// // 不论按什么键都触发
// (event: any) => {
// const { value } = event.target;
// console.log('ok',value)
// },
// {
// target:inputRef,
// }
// )
还是按照原来的方法,支持任意键的操作
// 监听组合键
import { useEffect,useRef,useState } from "react"
function isType(obj){
return Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/,'$1').toLowerCase()
}
export default function useKeyBindings(keyCodes,isOrder){
// keyCodes:数组,需要检测按下的键值的数组
// isOrder:是否完全顺序一致,比如顺序必须匹配,不能乱序,和给的数组必须完全匹配顺序
console.log('useKeyBindings')
const [match,setMatch]=useState(false)
const keyCodeRef=useRef([])
// keyCodeRef.current=[]
// 这样初始化会导致值重置的问题
console.log(keyCodeRef.current,'每次都会重置??')
useEffect(()=>{
document.addEventListener('keydown',handleKeyDown)
document.addEventListener('keyup',handleKeyUp)
function handleKeyDown(e){
const type=isType(keyCodes)
console.log(type)
if(type==='number'||type==='string'){
// 输入数字的时候
handleKeyDownClick(e,keyCodes)
}
if(type==='array'){
keyCodes.some((item)=>handleKeyDownClick(e,item))
}
}
function handleKeyUp(e){
console.log(e)
setMatch(false)
//感觉打印还是有点问题啊。
keyCodeRef.current=[]
//这里还是不置为空,先是把那个按键取出来
// if(keyCodeRef.current.length==1){
// keyCodeRef.current=[]
// }else if(e.keyCode){
// }
}
function handleKeyDownClick(e,keyCodes){
const type=isType(keyCodes)
console.log('handleClick')
if(type==='number'){
return e.keyCode==keyCodes
}
const keyCodesArr=keyCodes.split('.')
if(keyCodeRef.current.length<keyCodesArr.length){
if(!keyCodeRef.current.includes(e.key)){
keyCodeRef.current.push(e.key)
// 满足条件做一次检测
if(keyCodeRef.current.length===keyCodesArr.length){
console.log("check")
if(isOrder){
// 全匹配
for(let i=0;i<keyCodesArr.length;i++){
console.log(keyCodeRef.current)
console.log(keyCodesArr)
if(keyCodeRef.current[i]!==keyCodesArr[i]){
console.log('yes',i)
setMatch(false)
// return [match]
// break;
}
if(i==keyCodesArr.length-1){
setMatch(true)
// return [match]
}
}
}else{
for(let i=0;i<keyCodesArr.length;i++){
if(!keyCodesArr.includes(keyCodeRef.current[i])){
setMatch(false)
// return [match]
// break;
}
if(i==keyCodesArr.length-1){
setMatch(true)
// return [match]
}
}
}
}
}
}
}
return ()=>{
document.removeEventListener('keydown',handleKeyDown)
document.removeEventListener('keyup',handleKeyUp)
}
},[keyCodes,isOrder])
return [match]
}
//1.我这个函数修改一个值,但是反过来我这个函数依赖这个值进行再次渲染,这样会不会造成死循环
//键值对应:由于使用的是event.key这个参数用来对比,所以
// ctrl:Control
网友评论