├── .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 |
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 |
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 `