├── .editorconfig ├── .fatherrc.ts ├── .gitignore ├── .prettierignore ├── .prettierrc.js ├── .umirc.ts ├── README.md ├── docs └── index.md ├── package.json ├── src ├── assets │ ├── preview_bg.jpg │ ├── qdzc_logo.jpg │ ├── react-ihooks_bg.png │ └── react-ihooks_logo.png ├── hooks │ └── src │ │ ├── UseComponentDestroy │ │ └── index.tsx │ │ ├── UseDocumentTitle │ │ ├── demo │ │ │ └── demo1.tsx │ │ ├── index.md │ │ └── index.tsx │ │ ├── useDebounce │ │ ├── debounceOption.ts │ │ ├── demo │ │ │ └── demo1.tsx │ │ ├── index.md │ │ └── index.tsx │ │ ├── useDebounceFn │ │ └── index.tsx │ │ ├── useFullScreen │ │ ├── demo │ │ │ └── demo1.tsx │ │ ├── index.md │ │ ├── index.ts │ │ └── screenfull │ │ │ ├── index.d.ts │ │ │ ├── index.js │ │ │ ├── license │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── useLatest │ │ └── index.tsx │ │ ├── useMemoizedFn │ │ └── index.tsx │ │ ├── useThrottle │ │ ├── demo │ │ │ └── demo1.tsx │ │ ├── index.md │ │ └── index.tsx │ │ ├── useThrottleFn │ │ └── index.tsx │ │ ├── useTravel │ │ ├── demo │ │ │ ├── demo1.tsx │ │ │ └── demo2.tsx │ │ ├── index.md │ │ └── index.ts │ │ └── utils │ │ ├── domTarget.ts │ │ └── isBrowser.ts └── index.ts ├── tsconfig.json └── typings.d.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /.fatherrc.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | target: 'node', 3 | cjs: { type: 'babel', lazy: true }, 4 | disableTypeCheck: true, 5 | pkgs: ['hooks'], 6 | }; 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /npm-debug.log* 6 | /yarn-error.log 7 | /yarn.lock 8 | /package-lock.json 9 | 10 | # production 11 | /es 12 | /docs-dist 13 | /lib 14 | 15 | # misc 16 | .DS_Store 17 | /coverage 18 | 19 | # umi 20 | .umi 21 | .umi-production 22 | .umi-test 23 | .env.local 24 | 25 | # ide 26 | /.vscode 27 | /.idea 28 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/*.svg 2 | **/*.ejs 3 | **/*.html 4 | package.json 5 | .umi 6 | .umi-production 7 | .umi-test 8 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@umijs/fabric').prettier; 2 | -------------------------------------------------------------------------------- /.umirc.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'dumi'; 2 | 3 | export default defineConfig({ 4 | title: 'react-ihooks', 5 | favicon: 6 | 'https://raw.githubusercontent.com/00feng00/react-ihooks/main/src/assets/react-ihooks_bg.png', 7 | logo: 'https://raw.githubusercontent.com/00feng00/react-ihooks/main/src/assets/react-ihooks_bg.png', 8 | outputPath: 'docs-dist', 9 | mode: 'site', 10 | locales: [['zh-CN', '中文']], 11 | // resolve: { 12 | // includes: ['docs', 'hooks/src'], 13 | // }, 14 | navs: { 15 | 'zh-CN': [{ title: 'Hooks', path: '/hooks' }], 16 | }, 17 | menus: { 18 | '/hooks': [ 19 | { 20 | title: '设置标题', 21 | children: ['UseDocumentTitle'], 22 | }, 23 | { 24 | title: '防抖/节流', 25 | children: ['useDebounce', 'useThrottle'], 26 | }, 27 | // { 28 | // title: '全屏(已废弃)', 29 | // children: ['useFullScreen'], 30 | // }, 31 | { 32 | title: '管理历史状态', 33 | children: ['useTravel'], 34 | }, 35 | ], 36 | }, 37 | }); 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-ihooks 2 | 3 | [文档预览](https://react-ihooks.github.io/)
4 | 5 | 6 | 7 | ## 安装 react-ihooks 8 | 9 | ```bash 10 | $ npm i react-ihooks 11 | ``` 12 | 13 | ## useDocumentTitle 14 | 15 | 示例代码 16 | 17 | ```tsx 18 | import React from 'react'; 19 | import { useDocumentTitle } from 'react-ihooks'; 20 | 21 | export default () => { 22 | useDocumentTitle('页面标题'); 23 | 24 | return ( 25 |
26 |

为页面设置标题

27 |
28 | ); 29 | }; 30 | ``` 31 | 32 | ## useDebounce 33 | 34 | 示例代码 35 | 36 | ```tsx 37 | import React, { useState } from 'react'; 38 | import { useDebounce } from 'react-ihooks'; 39 | 40 | export default () => { 41 | const [value, setValue] = useState(''); 42 | const debouncedValue = useDebounce(value, { wait: 200 }); 43 | 44 | return ( 45 |
46 | setValue(e.target.value)} 49 | placeholder="请输入信息" 50 | style={{ width: 180 }} 51 | /> 52 |

DebouncedValue: {debouncedValue}

53 |
54 | ); 55 | }; 56 | ``` 57 | 58 | ## 技术交流 59 | 60 | 61 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | hero: 3 | title: react-ihooks 4 | desc: 前端早茶的React函数库-react-ihooks 5 | actions: 6 | - text: 快速开始 7 | link: /hooks 8 | features: 9 | - icon: https://gw.alipayobjects.com/zos/bmw-prod/881dc458-f20b-407b-947a-95104b5ec82b/k79dm8ih_w144_h144.png 10 | title: 基于React 11 | desc: hooks 12 | - icon: https://avatars.githubusercontent.com/u/18207644?v=4 13 | title: 前端早茶 14 | desc: 广东靓仔 15 | - icon: https://gw.alipayobjects.com/zos/bmw-prod/d1ee0c6f-5aed-4a45-a507-339a4bfe076c/k7bjsocq_w144_h144.png 16 | title: 高阶函数 17 | desc: 通用 18 | footer: Open-source MIT Licensed | Copyright © 2020
Powered by [dumi](https://d.umijs.org) 19 | --- 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "react-ihooks", 4 | "version": "0.0.1", 5 | "scripts": { 6 | "start": "dumi dev", 7 | "docs:build": "dumi build", 8 | "docs:deploy": "gh-pages -d docs-dist", 9 | "build": "father-build", 10 | "deploy": "npm run docs:build && npm run docs:deploy", 11 | "prettier": "prettier --write \"**/*.{js,jsx,tsx,ts,less,md,json}\"", 12 | "test": "umi-test", 13 | "test:coverage": "umi-test --coverage", 14 | "prepublishOnly": "npm run build" 15 | }, 16 | "module": "es/index.js", 17 | "typings": "es/index.d.ts", 18 | "gitHooks": { 19 | "pre-commit": "lint-staged" 20 | }, 21 | "lint-staged": { 22 | "*.{js,jsx,less,md,json}": [ 23 | "prettier --write" 24 | ], 25 | "*.ts?(x)": [ 26 | "prettier --parser=typescript --write" 27 | ] 28 | }, 29 | "dependencies": { 30 | "react": "^16.12.0 || ^17.0.0" 31 | }, 32 | "devDependencies": { 33 | "@testing-library/jest-dom": "^5.15.1", 34 | "@testing-library/react": "^12.1.2", 35 | "@types/jest": "^27.0.3", 36 | "@types/lodash.debounce": "^4.0.6", 37 | "@types/lodash.throttle": "^4.1.6", 38 | "@umijs/fabric": "^2.8.1", 39 | "@umijs/test": "^3.0.5", 40 | "dumi": "^1.1.0", 41 | "father-build": "^1.17.2", 42 | "gh-pages": "^3.0.0", 43 | "lint-staged": "^10.0.7", 44 | "prettier": "^2.2.1", 45 | "yorkie": "^2.0.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/assets/preview_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/00feng00/react-ihooks/548be86345c95ba91726520619d182c2b5d13c25/src/assets/preview_bg.jpg -------------------------------------------------------------------------------- /src/assets/qdzc_logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/00feng00/react-ihooks/548be86345c95ba91726520619d182c2b5d13c25/src/assets/qdzc_logo.jpg -------------------------------------------------------------------------------- /src/assets/react-ihooks_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/00feng00/react-ihooks/548be86345c95ba91726520619d182c2b5d13c25/src/assets/react-ihooks_bg.png -------------------------------------------------------------------------------- /src/assets/react-ihooks_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/00feng00/react-ihooks/548be86345c95ba91726520619d182c2b5d13c25/src/assets/react-ihooks_logo.png -------------------------------------------------------------------------------- /src/hooks/src/UseComponentDestroy/index.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react'; 2 | 3 | /** 4 | * @description: 组件页面卸载 5 | * @param {function} fn 6 | * @return {*} 7 | */ 8 | const useComponentDestroy = (fn: () => void) => { 9 | const ref = useRef(fn); 10 | ref.current = fn; 11 | const fnRef = ref; 12 | 13 | useEffect(() => { 14 | () => { 15 | fnRef.current(); 16 | }; 17 | }, []); 18 | }; 19 | 20 | export default useComponentDestroy; 21 | -------------------------------------------------------------------------------- /src/hooks/src/UseDocumentTitle/demo/demo1.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useDocumentTitle } from 'react-ihooks'; 3 | 4 | export default () => { 5 | useDocumentTitle('页面标题'); 6 | 7 | return ( 8 |
9 |

为页面设置标题

10 |
11 | ); 12 | }; 13 | -------------------------------------------------------------------------------- /src/hooks/src/UseDocumentTitle/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /hooks 4 | --- 5 | 6 | ## useDocumentTitle 7 | 8 | 设置浏览器页面标题 9 | 10 | ### 基础用法 11 | 12 | Demo: 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/hooks/src/UseDocumentTitle/index.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react'; 2 | import isBrowser from '../utils/isBrowser'; 3 | import useComponentDestroy from '../useComponentDestroy'; 4 | export interface Options { 5 | recoverOnDestroy?: boolean; 6 | } 7 | const DEFAULT_OPTION: Options = { 8 | recoverOnDestroy: false, 9 | }; 10 | /** 11 | * @description: 设置浏览器标题 12 | * @param {*} title 标题 13 | * @param {*} options 选项 14 | * @return {*} 15 | */ 16 | function useDocumentTitle(title: string, options: Options = DEFAULT_OPTION) { 17 | const documentTitleRef = useRef(isBrowser ? document.title : ''); 18 | 19 | useEffect(() => { 20 | document.title = title; 21 | }, [title]); 22 | 23 | // 页面卸载,是否恢复上一个页面的标题 24 | useComponentDestroy(() => { 25 | if (options.recoverOnDestroy) { 26 | document.title = documentTitleRef.current; 27 | } 28 | }); 29 | } 30 | export default useDocumentTitle; 31 | -------------------------------------------------------------------------------- /src/hooks/src/useDebounce/debounceOption.ts: -------------------------------------------------------------------------------- 1 | export interface DebounceOptions { 2 | wait?: number; 3 | leading?: boolean; 4 | trailing?: boolean; 5 | maxWait?: number; 6 | } 7 | -------------------------------------------------------------------------------- /src/hooks/src/useDebounce/demo/demo1.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { useDebounce } from 'react-ihooks'; 3 | 4 | export default () => { 5 | const [value, setValue] = useState(); 6 | const debouncedValue = useDebounce(value, { wait: 500 }); 7 | 8 | return ( 9 |
10 | setValue(e.target.value)} 13 | placeholder="Typed value" 14 | style={{ width: 280 }} 15 | /> 16 |

DebouncedValue: {debouncedValue}

17 |
18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /src/hooks/src/useDebounce/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /hooks 4 | --- 5 | 6 | ## useDebounce 7 | 8 | 定制处理防抖: ·设置等待时间, ·是否在延迟开始前调用函数 ·是否在延迟开始后调用函数 ·最大等待时间,单位为毫秒 9 | 10 | ### 基础用法 11 | 12 | Demo: 13 | 14 | 15 | 16 | ## API 17 | 18 | ```typescript 19 | const debouncedValue = useDebounce( 20 | value: any, 21 | options?: Options 22 | ); 23 | ``` 24 | 25 | ### Params 26 | 27 | | Property | Description | Type | Default | 28 | | -------- | ---------------------------------- | --------- | ------- | 29 | | value | The value to debounce. | `any` | - | 30 | | options | Config for the debounce behaviors. | `Options` | - | 31 | 32 | ### Options 33 | 34 | | Property | Description | Type | Default | 35 | | --- | --- | --- | --- | 36 | | wait | The number of milliseconds to delay. | `number` | `1000` | 37 | | leading | Specify invoking on the leading edge of the timeout. | `boolean` | `false` | 38 | | trailing | Specify invoking on the trailing edge of the timeout. | `boolean` | `true` | 39 | | maxWait | The maximum time func is allowed to be delayed before it’s invoked. | `number` | - | 40 | -------------------------------------------------------------------------------- /src/hooks/src/useDebounce/index.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import useDebounceFn from '../useDebounceFn'; 3 | import { DebounceOptions } from './debounceOption'; 4 | 5 | function useDebounce(value: T, options?: DebounceOptions) { 6 | const [debounced, setDebounced] = useState(value); 7 | 8 | const { run } = useDebounceFn(() => { 9 | setDebounced(value); 10 | }, options); 11 | 12 | useEffect(() => { 13 | run(); 14 | }, [value]); 15 | 16 | return debounced; 17 | } 18 | 19 | export default useDebounce; 20 | -------------------------------------------------------------------------------- /src/hooks/src/useDebounceFn/index.tsx: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | import debounce from 'lodash/debounce'; 3 | import useComponentDestroy from '../useComponentDestroy'; 4 | import useLatest from '../useLatest'; 5 | import { DebounceOptions } from '../useDebounce/debounceOption'; 6 | 7 | type noop = (...arg: any) => any; 8 | 9 | /** 10 | * @description: 处理防抖函数逻辑 11 | * @param {T} fn 12 | * @param {DebounceOptions} options 13 | * @return {*} 14 | */ 15 | function useDebounceFn(fn: T, options?: DebounceOptions) { 16 | const fnRef = useLatest(fn); 17 | // 默认等1s 18 | const wait = options?.wait ?? 1000; 19 | 20 | const debounced = useMemo( 21 | () => 22 | debounce( 23 | ((...arg: any[]) => { 24 | return fnRef.current(...arg); 25 | }) as T, 26 | wait, 27 | options, 28 | ), 29 | [], 30 | ); 31 | // 页面销毁,取消防抖 32 | useComponentDestroy(() => { 33 | debounced.cancel(); 34 | }); 35 | 36 | return { 37 | run: debounced as unknown as T, 38 | cancel: debounced.cancel, 39 | flush: debounced.flush, 40 | }; 41 | } 42 | export default useDebounceFn; 43 | -------------------------------------------------------------------------------- /src/hooks/src/useFullScreen/demo/demo1.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * title: Default usage 3 | * desc: Use ref to set elements that need full screen 4 | * 5 | * title.zh-CN: 基础用法 6 | * desc.zh-CN: 使用 ref 设置需要全屏的元素 7 | */ 8 | 9 | import React, { useRef } from 'react'; 10 | import { useFullScreen } from 'react-ihooks'; 11 | 12 | export default () => { 13 | const ref = useRef(); 14 | const [isFullscreen, { enterFullscreen, exitFullscreen, toggleFullscreen }] = useFullScreen(ref); 15 | return ( 16 |
17 |
{isFullscreen ? 'Fullscreen' : 'Not fullscreen'}
18 |
19 | 22 | 25 | 28 |
29 |
30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /src/hooks/src/useFullScreen/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /hooks 4 | --- 5 | 6 | # useFullscreen 7 | 8 | 管理 DOM 全屏的 Hook。 9 | 10 | ## 代码演示 11 | 12 | ### 基础用法 13 | 14 | 15 | 16 | ## API 17 | 18 | ```typescript 19 | const [ 20 | isFullscreen, 21 | { 22 | enterFullscreen, 23 | exitFullscreen, 24 | toggleFullscreen, 25 | isEnabled, 26 | }] = useFullScreen( 27 | target, 28 | options?: Options 29 | ); 30 | ``` 31 | 32 | ### Params 33 | 34 | | 参数 | 说明 | 类型 | 默认值 | 35 | | --- | --- | --- | --- | 36 | | target | DOM 节点或者 ref | `Element` \| `() => Element` \| `MutableRefObject` | - | 37 | | options | 设置 | `Options` | - | 38 | 39 | ### Options 40 | 41 | | 参数 | 说明 | 类型 | 默认值 | 42 | | ------- | ------------ | ------------ | ------ | 43 | | onExit | 退出全屏触发 | `() => void` | - | 44 | | onEnter | 全屏触发 | `() => void` | - | 45 | 46 | ### Result 47 | 48 | | 参数 | 说明 | 类型 | 49 | | ---------------- | ------------ | ------------ | 50 | | isFullscreen | 是否全屏 | `boolean` | 51 | | enterFullscreen | 设置全屏 | `() => void` | 52 | | exitFullscreen | 退出全屏 | `() => void` | 53 | | toggleFullscreen | 切换全屏 | `() => void` | 54 | | isEnabled | 是否支持全屏 | `boolean` | 55 | -------------------------------------------------------------------------------- /src/hooks/src/useFullScreen/index.ts: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import screenfull from './screenfull/index.js'; 3 | import useLatest from '../useLatest'; 4 | import useComponentDestroy from '../useComponentDestroy'; 5 | import type { BasicTarget } from '../utils/domTarget'; 6 | import { getTargetElement } from '../utils/domTarget'; 7 | import useMemoizedFn from '../useMemoizedFn'; 8 | 9 | export interface Options { 10 | onExit?: () => void; 11 | onEnter?: () => void; 12 | } 13 | 14 | const useFullScreen = (target: BasicTarget, options: Options) => { 15 | const { onExit, onEnter } = options || {}; 16 | 17 | const onExitRef = useLatest(onExit); 18 | const onEnterRef = useLatest(onEnter); 19 | 20 | const [state, setState] = useState(false); 21 | 22 | const onChange = () => { 23 | if (screenfull.isEnabled) { 24 | const { isFullscreen } = screenfull; 25 | if (isFullscreen) { 26 | onEnterRef.current?.(); 27 | } else { 28 | screenfull.off('change', onChange); 29 | onExitRef.current?.(); 30 | } 31 | setState(isFullscreen); 32 | } 33 | }; 34 | 35 | const enterFullscreen = () => { 36 | const el = getTargetElement(target); 37 | if (!el) { 38 | return; 39 | } 40 | 41 | if (screenfull.isEnabled) { 42 | try { 43 | screenfull.request(el); 44 | screenfull.on('change', onChange); 45 | } catch (error) { 46 | console.error(error); 47 | } 48 | } 49 | }; 50 | 51 | const exitFullscreen = () => { 52 | if (!state) { 53 | return; 54 | } 55 | if (screenfull.isEnabled) { 56 | screenfull.exit(); 57 | } 58 | }; 59 | 60 | const toggleFullscreen = () => { 61 | if (state) { 62 | exitFullscreen(); 63 | } else { 64 | enterFullscreen(); 65 | } 66 | }; 67 | 68 | useComponentDestroy(() => { 69 | if (screenfull.isEnabled) { 70 | screenfull.off('change', onChange); 71 | } 72 | }); 73 | 74 | return [ 75 | state, 76 | { 77 | enterFullscreen: useMemoizedFn(enterFullscreen), 78 | exitFullscreen: useMemoizedFn(exitFullscreen), 79 | toggleFullscreen: useMemoizedFn(toggleFullscreen), 80 | isEnabled: screenfull.isEnabled, 81 | }, 82 | ] as const; 83 | }; 84 | 85 | export default useFullScreen; 86 | -------------------------------------------------------------------------------- /src/hooks/src/useFullScreen/screenfull/index.d.ts: -------------------------------------------------------------------------------- 1 | export type RawEventNames = { 2 | readonly requestFullscreen: string; 3 | readonly exitFullscreen: string; 4 | readonly fullscreenElement: string; 5 | readonly fullscreenEnabled: string; 6 | readonly fullscreenchange: string; 7 | readonly fullscreenerror: string; 8 | }; 9 | 10 | export type EventName = 'change' | 'error'; 11 | 12 | /** 13 | Simple wrapper for cross-browser usage of the JavaScript [Fullscreen API](https://developer.mozilla.org/en/DOM/Using_full-screen_mode), which lets you bring the page or any element into fullscreen. Smoothens out the browser implementation differences, so you don't have to. 14 | */ 15 | declare const screenfull: { 16 | /** 17 | Whether fullscreen is active. 18 | */ 19 | readonly isFullscreen: boolean; 20 | 21 | /** 22 | The element currently in fullscreen, otherwise `undefined`. 23 | */ 24 | readonly element: Element | undefined; 25 | 26 | /** 27 | Whether you are allowed to enter fullscreen. If your page is inside an `