useKeyPress

2020-07-28  本文已影响0人  skoll
image.png

//为什么逻辑感觉都走完了,还会打印出来一遍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

上一篇 下一篇

猜你喜欢

热点阅读