React Hooks - 自定义

96 阅读2分钟
  • 以 “use” 开头、 提炼复用逻辑
  • 在函数最外层调⽤ Hook、且只能在函数组件中使用
自定义 useState
import { useEffect, useRef, useState } from 'react'
 
const useXState = (initState) => {
    const [state, setState] = useState(initState);
    let isUpdate = useRef()
    
    const setXState = (state, cb) => {
        setState(prev => {
            isUpdate.current = cb
            return typeof state === 'function' ? state(prev) : state
        })
    }
    
    useEffect(() => {
        if(isUpdate.current) {
            isUpdate.current()
        }
    })
    
    return [state, setXState]
}
 
export default useXState

自定义 定时器

// ⾃定义hook - 定制器
function useClock() {
    const [date, setDate] = useState(new Date());
    useEffect(() => {
        // didMount时执行
        const timer = setInterval(() => {
            setDate(new Date());
        }, 1000);
        
        // 清除定时器,类似willUnmount
        return () => clearInterval(timer);
    }, []);
    return date;
}

// 使用⾃定义hook
import React, { useState, useEffect, useMemo } from "react";
export default function CustomHookPage(props) {
    const [count, setCount] = useState(0);
    // 副作用函数
    useEffect(() => {
        document.title = `点击了${count}次`;
    }, [count]); // 依赖项 count
    
    return (
        <div>
            <p>{count}</p>
            <button onClick={() => setCount(count + 1)}>add</button>
            <p>{useClock().toLocaleTimeString()}</p>
        </div>
    );
}

自定义 动态切换标题

// ⾃定义hook - title
import { useEffect } from 'react'
const useTitle = (title) => {
    useEffect(() => {
        document.title = title
    }, [])
    return
}
export default useTitle


// 使用⾃定义hook
const Home = () => {
    useTitle('趣谈前端')
    return <div>home</div>
}

自定义 更新组件( forceUpdate )

// ⾃定义hook - forceUpdate 
import { useState } from 'react'
const useUpdate = () => {
    const [, setFlag] = useState()
    const update = () => {
        setFlag(Date.now())
    }
    return update
}
export default useUpdate


// 使用⾃定义hook
const Home = (props) => {
    const update = useUpdate()
    return (
        <div>
            {Date.now()}
            <div><button onClick={update}>update</button></div>
        </div>
    )
}

自定义 监听窗口

// ⾃定义hook - 监听窗口
import React, { useState, useCallback, useEffect } from "react";
export const useWinSize = () => {
    const [size, setSize] = useState({
        width: document.documentElement.clientWidth,
        height: document.documentElement.clientHeight
    });
    const changeSize = useCallback(() => {
        setSize({
            width: document.documentElement.clientWidth,
            height: document.documentElement.clientHeight
        });
    }, []);
    useEffect(() => {
        window.addEventListener("resize", changeSize);
        return () => {
            window.removeEventListener("resize", changeSize);
        };
    }, []);
    return size;
};

// 使用⾃定义hook
import React, { useRef } from 'react'
import { useWinSize } from "./customHook/useWinSize";
function App(props) {
    const size = useWinSize();
    return (
        <div >
            页面大小: `{size.width}*{size.height}`
        </div>
    )
}
export default App;

自定义 节流防抖

// 自定义Hook - 节流
import { useEffect, useRef, useState } from 'react'
const useThrottle = (fn, ms = 30, deps = []) => {
    let previous = useRef(0)
    let [time, setTime] = useState(ms)
    useEffect(() => {
        let now = Date.now();
        if (now - previous.current > time) {
            fn();
            previous.current = now;
        }
    }, deps) // 依赖更新
    
    const cancel = () => {
        setTime(0)
    }
    return [cancel]
}
export default useThrottle

自定义 防抖

// js写法
import { useEffect, useRef } from 'react'
const useDebounce = (fn, ms = 30, deps = []) => {
    let timeout = useRef()
    useEffect(() => {
        if (timeout.current) {
            clearTimeout(timeout.current)
            timeout.current = null;
        }
        timeout.current = setTimeout(() => {
            fn()
        }, ms)
    }, deps)
    const cancel = () => {
        clearTimeout(timeout.current)
        timeout.current = null
    }
    return [cancel]
}
export default useDebounce
// ts 写法
import { useEffect, useRef } from 'react';

export default (fn: () => void, ms = 200, deps: string[] = []) => {
  const timeout = useRef<ReturnType<typeof setTimeout> | null>(null);

  useEffect(() => {
    if (timeout.current) {
      clearTimeout(timeout.current);
      timeout.current = null;
    }

    timeout.current = setTimeout(() => {
      fn();
    }, ms);
  }, deps);

  const cancel = () => {
    clearTimeout(timeout.current as ReturnType<typeof setTimeout>);
    timeout.current = null;
  };

  return [cancel];
};
// 使用⾃定义hook
const Home = (props) => {
    const [a, setA] = useState(0)
    const [b, setB] = useState(0)
    const [cancel] = useDebounce(() => {
        setB(a)
    }, 2000, [a])
    
    const changeIpt = (e) => {
        setA(e.target.value)
    }
    
    return (
        <div>
            { b } { a }
            <input type="text" onChange={changeIpt} />
        </div>
    )
}