├── .browserslistrc
├── pnpm-workspace.yaml
├── scripts
└── generate
│ ├── templates
│ ├── readme.tpl
│ ├── index.tpl
│ ├── test-dom.tpl
│ ├── eslint.tpl
│ ├── test-hook.tpl
│ ├── tsconfig.tpl
│ └── demo.tpl
│ ├── template.js
│ ├── dependencies.js
│ └── prompt.js
├── packages
├── router
│ ├── src
│ │ ├── index.ts
│ │ └── url.ts
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── docs
│ │ ├── useSearchParamAll.md
│ │ ├── useSearchParam.md
│ │ ├── useNavigate.md
│ │ ├── useSearchParamState.md
│ │ ├── useUpdateSearchParams.md
│ │ └── demo
│ │ │ └── useSearchParamState.tsx
│ └── README.md
├── boolean
│ ├── .eslintrc.js
│ ├── docs
│ │ ├── demo
│ │ │ ├── useBoolean.less
│ │ │ └── useBoolean.tsx
│ │ ├── useToggle.md
│ │ ├── useSwitch.md
│ │ └── useBoolean.md
│ ├── tsconfig.json
│ ├── README.md
│ └── src
│ │ └── index.ts
├── debounce
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ └── docs
│ │ ├── useDebouncedValue.md
│ │ ├── useDebouncedEffect.md
│ │ ├── demo
│ │ ├── useDebouncedValue.tsx
│ │ └── useDebouncedEffect.tsx
│ │ └── useDebouncedCallback.md
├── debug
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ └── docs
│ │ ├── useRenderTimes.md
│ │ ├── demo
│ │ ├── useRenderTimes.tsx
│ │ └── useChangeTimes.tsx
│ │ └── useChangeTimes.md
├── hover
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── docs
│ │ ├── demo
│ │ │ ├── useHover.tsx
│ │ │ └── useHover2.tsx
│ │ └── useHover.md
│ └── package.json
├── immer
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ └── docs
│ │ ├── useImmerState.md
│ │ ├── useImmerReducer.md
│ │ └── demo
│ │ ├── useImmerState.tsx
│ │ └── useImmerReducer.tsx
├── index
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ └── README.md
├── media
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── docs
│ │ ├── useMedia.md
│ │ ├── demo
│ │ │ ├── usePreferDarkMode.tsx
│ │ │ └── useMedia.tsx
│ │ └── usePreferDarkMode.md
│ ├── src
│ │ └── index.ts
│ ├── demo
│ │ └── entries
│ │ │ └── index.js
│ └── package.json
├── methods
│ ├── .eslintrc.js
│ ├── src
│ │ ├── index.ts
│ │ ├── interface.ts
│ │ └── native.ts
│ ├── tsconfig.json
│ ├── docs
│ │ ├── useMethodsExtensionNative.md
│ │ ├── useMethodsNative.md
│ │ └── useMethodsExtension.md
│ └── README.md
├── network
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── docs
│ │ ├── useOnLine.md
│ │ └── demo
│ │ │ └── useOnLine.tsx
│ └── src
│ │ └── index.ts
├── number
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── docs
│ │ ├── demo
│ │ │ └── useCounter.tsx
│ │ └── useCounter.md
│ └── src
│ │ └── index.ts
├── poll
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── docs
│ │ └── demo
│ │ │ ├── usePoll.tsx
│ │ │ └── usePoll2.tsx
│ └── demo
│ │ └── entries
│ │ └── index.js
├── request
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ └── docs
│ │ ├── demo
│ │ └── useRequest.tsx
│ │ └── useRequestCallback.md
├── script
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── demo
│ │ ├── components
│ │ │ ├── SimpleScript
│ │ │ │ └── index.js
│ │ │ └── WithSuspense
│ │ │ │ └── index.js
│ │ └── entries
│ │ │ └── index.js
│ ├── settings.js
│ └── docs
│ │ └── useScriptSuspense.md
├── snapshot
│ ├── .eslintrc.js
│ ├── src
│ │ └── interface.ts
│ ├── tsconfig.json
│ ├── README.md
│ ├── docs
│ │ ├── demo
│ │ │ └── useSnapshotState2.tsx
│ │ └── useSnapshotState.md
│ └── package.json
├── timeout
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── docs
│ │ ├── useStableInterval.md
│ │ ├── useTimeout.md
│ │ ├── useInterval.md
│ │ └── demo
│ │ │ ├── useInterval.tsx
│ │ │ └── useTimeout.tsx
│ └── package.json
├── update
│ ├── .eslintrc.js
│ ├── src
│ │ ├── index.ts
│ │ └── __tests__
│ │ │ └── index.test.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── docs
│ │ ├── demo
│ │ │ └── useForceUpdate.tsx
│ │ └── useForceUpdate.md
│ └── package.json
├── click-outside
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── src
│ │ ├── index.ts
│ │ └── __tests__
│ │ │ └── index.test.js
│ └── docs
│ │ └── useClickOutside.md
├── collection
│ ├── .eslintrc.js
│ ├── src
│ │ └── index.ts
│ ├── tsconfig.json
│ ├── docs
│ │ ├── demo
│ │ │ ├── useArray.less
│ │ │ └── useArray.tsx
│ │ ├── useSet.md
│ │ ├── useMap.md
│ │ └── useArray.md
│ └── README.md
├── derived-state
│ ├── .eslintrc.js
│ ├── docs
│ │ └── demo
│ │ │ └── useDerivedState.less
│ ├── tsconfig.json
│ ├── README.md
│ ├── src
│ │ ├── index.ts
│ │ └── __tests__
│ │ │ └── index.test.js
│ └── package.json
├── effect-ref
│ ├── .eslintrc.js
│ ├── docs
│ │ ├── demo
│ │ │ ├── useEffectRef.less
│ │ │ └── useEffectRef.tsx
│ │ └── useEffectRef.md
│ ├── tsconfig.json
│ ├── README.md
│ └── package.json
├── element-size
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ └── docs
│ │ ├── demo
│ │ ├── useElementSize.tsx
│ │ └── useElementResize.tsx
│ │ ├── useElementSize.md
│ │ └── useElementResize.md
├── input-value
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── src
│ │ ├── index.ts
│ │ └── __tests__
│ │ │ └── index.test.js
│ ├── docs
│ │ ├── useInputValue.md
│ │ └── demo
│ │ │ └── useInputValue.tsx
│ └── package.json
├── intended-lazy
│ ├── .eslintrc.js
│ ├── README.md
│ ├── tsconfig.json
│ ├── src
│ │ └── index.ts
│ ├── docs
│ │ ├── useIntendedLazyCallback.md
│ │ └── useIntendedLazyValue.md
│ ├── CHANGELOG.md
│ └── package.json
├── intersection
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── docs
│ │ ├── demo
│ │ │ ├── useOnScreen.less
│ │ │ └── useOnScreen.tsx
│ │ ├── useOnScreenCallback.md
│ │ └── useOnScreen.md
│ └── demo
│ │ └── entries
│ │ └── index.js
├── local-storage
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ └── docs
│ │ ├── useLocalStorage.md
│ │ └── demo
│ │ └── useLocalStorage.tsx
├── merged-ref
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── docs
│ │ ├── useMergedRef.md
│ │ └── demo
│ │ │ └── useMergedRef.tsx
│ └── package.json
├── optimistic
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── src
│ │ └── interface.ts
│ ├── docs
│ │ └── useOptimisticTask.md
│ └── package.json
├── performance
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── docs
│ │ ├── useLayoutTiming.md
│ │ ├── demo
│ │ │ └── useLayoutTiming.tsx
│ │ └── usePerformanceTiming.md
│ └── package.json
├── scroll-lock
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── docs
│ │ ├── useScrollLock.md
│ │ └── demo
│ │ │ └── useScrollLock.tsx
│ ├── src
│ │ ├── index.ts
│ │ └── __tests__
│ │ │ └── index.test.js
│ └── package.json
├── user-media
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ └── docs
│ │ └── useUserMedia.md
├── web-socket
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── src
│ │ ├── constants.ts
│ │ └── interface.ts
│ └── README.md
├── window-size
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── docs
│ │ ├── demo
│ │ │ └── useWindowSize.tsx
│ │ └── useWindowSize.md
│ └── src
│ │ ├── __tests__
│ │ └── index.test.js
│ │ └── index.ts
├── action-pending
│ ├── .eslintrc.js
│ ├── docs
│ │ ├── demo
│ │ │ ├── useActionPending.less
│ │ │ └── useActionPending.tsx
│ │ ├── useActionPending.zh-CN.md
│ │ └── useActionPending.md
│ ├── tsconfig.json
│ ├── README.md
│ └── src
│ │ ├── __tests__
│ │ └── index.test.js
│ │ └── index.ts
├── document-event
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── docs
│ │ ├── useDocumentEvent.md
│ │ └── demo
│ │ │ └── useDocumentEvent.tsx
│ ├── src
│ │ └── index.ts
│ └── package.json
├── document-title
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── src
│ │ ├── index.ts
│ │ └── __tests__
│ │ │ └── index.test.js
│ ├── docs
│ │ ├── useDocumentTitle.md
│ │ └── demo
│ │ │ └── useDocumentTitle.tsx
│ └── package.json
├── infinite-scroll
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── demo
│ │ └── entries
│ │ │ └── index.less
│ └── README.md
├── previous-value
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ └── docs
│ │ ├── usePreviousEquals.md
│ │ ├── useOriginalDeepCopy.md
│ │ ├── demo
│ │ ├── usePreviousValue.tsx
│ │ └── useOriginalCopy.tsx
│ │ └── usePreviousValue.md
├── scroll-into-view
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── src
│ │ ├── index.ts
│ │ └── __tests__
│ │ │ └── index.test.js
│ ├── demo
│ │ └── entries
│ │ │ └── index.less
│ ├── docs
│ │ └── useScrollIntoView.md
│ └── package.json
├── scroll-position
│ ├── .eslintrc.js
│ ├── src
│ │ └── has-passive-events.d.ts
│ ├── tsconfig.json
│ ├── docs
│ │ ├── useScrollTop.md
│ │ ├── useScrollLeft.md
│ │ ├── demo
│ │ │ └── useScrollPosition.tsx
│ │ └── useScrollPosition.md
│ ├── README.md
│ └── package.json
├── transition-state
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── README.md
│ ├── docs
│ │ ├── useTransitionState.md
│ │ └── demo
│ │ │ └── useTransitionState.tsx
│ ├── src
│ │ └── index.ts
│ └── package.json
└── selection
│ ├── tsconfig.json
│ ├── docs
│ ├── demo
│ │ └── useSelection.less
│ └── useSelection.md
│ └── README.md
├── public
└── style.css
├── lerna.json
├── docs
├── docs
│ ├── packages.zh-CN.md
│ └── packages.en-US.md
├── index.zh-CN.md
└── index.en-US.md
├── tsconfig.json
├── .github
└── workflows
│ ├── ci.yml
│ └── doc.yml
├── LICENSE
└── .gitignore
/.browserslistrc:
--------------------------------------------------------------------------------
1 | node >= 12
2 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - 'packages/*'
3 |
--------------------------------------------------------------------------------
/scripts/generate/templates/readme.tpl:
--------------------------------------------------------------------------------
1 | # @huse/%packageName%
2 |
--------------------------------------------------------------------------------
/scripts/generate/templates/index.tpl:
--------------------------------------------------------------------------------
1 | export function %hookName%() {
2 | }
3 |
--------------------------------------------------------------------------------
/packages/router/src/index.ts:
--------------------------------------------------------------------------------
1 | /* istanbul ignore file */
2 | export * from './navigate';
3 | export * from './search';
4 |
--------------------------------------------------------------------------------
/packages/boolean/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/debounce/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/debug/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/hover/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/immer/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/index/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/media/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/methods/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/network/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/number/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/poll/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/request/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/router/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/script/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/snapshot/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/timeout/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/update/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/scripts/generate/templates/test-dom.tpl:
--------------------------------------------------------------------------------
1 | import {render} from '@testing-library/react';
2 | import {%hookName%} from '../index';
3 |
--------------------------------------------------------------------------------
/packages/click-outside/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/collection/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/derived-state/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/effect-ref/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/element-size/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/input-value/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/intended-lazy/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/intersection/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/local-storage/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/merged-ref/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/optimistic/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/performance/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/scroll-lock/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/user-media/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/web-socket/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/window-size/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/action-pending/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/document-event/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/document-title/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/infinite-scroll/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/previous-value/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/scroll-into-view/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/scroll-position/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/transition-state/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/scripts/generate/templates/eslint.tpl:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@reskript/config-lint/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/scripts/generate/templates/test-hook.tpl:
--------------------------------------------------------------------------------
1 | import {renderHook} from '@testing-library/react-hooks';
2 | import {%hookName%} from '../index';
3 |
--------------------------------------------------------------------------------
/packages/methods/src/index.ts:
--------------------------------------------------------------------------------
1 | /* istanbul ignore file */
2 | export * from './interface';
3 | export * from './native';
4 | export * from './immer';
5 |
--------------------------------------------------------------------------------
/packages/snapshot/src/interface.ts:
--------------------------------------------------------------------------------
1 | export interface SnapshotState Outer: {size.outerWidth} x {size.outerHeight} Inner: {size.innerWidth} x {size.innerHeight} Resize window to update Rendered {renderTimes.current} times Result: {previousValue === undefined ? value : `${previousValue} -> ${value}`} = (propValue: P, stateValue: S | undefined) => S;
4 |
5 | export function useDerivedState (
6 | propValue: P,
7 | compute: Derive = v => v as unknown as S
8 | ): [S, Dispatch
11 | Visual port is currently 👉
12 | {isOnScreen ? 'on' : 'out of'} screen
13 | {timing.end} (end) - {timing.start} (start) = {timing.ellapsed} (ellapsed) Current value: Scroll in the color block scrollTop: {position.y} scrollLeft: {position.x} (Leave this page will reset
22 | Your device: {deviceType}
23 |
12 | Possible Device:
13 | {isMobile ? 'Phone' : (isPad ? 'Pad' : 'Desktop')}
14 |
16 | Color Scheme:
17 | {isDarkMode ? 'Dark' : 'Light'}
18 |
14 |
18 |
19 |
--------------------------------------------------------------------------------
/packages/debug/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: README
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Debug
8 | path: /debug
9 | order: 1
10 | ---
11 |
12 | # Debug
13 |
14 | A set of hooks for debugging component.
15 |
16 | Since this package is for debugging, we will not consider the size of package as an important factor, please remember to remove all debug hooks in production.
17 |
18 | ```shell
19 | npm install @huse/debug
20 | ```
--------------------------------------------------------------------------------
/scripts/generate/template.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const fs = require('fs-extra');
3 | const replace = (input, vars) => input.replace(/%(\w+)%/g, (match, name) => vars[name]);
4 |
5 | module.exports = async (name, destination, vars) => {
6 | await fs.ensureFile(destination);
7 | const template = fs.readFileSync(path.join(__dirname, 'templates', `${name}.tpl`), 'utf-8');
8 | await fs.writeFile(destination, replace(template, vars));
9 | };
10 |
--------------------------------------------------------------------------------
/packages/immer/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: README
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Immer
8 | path: /immer
9 | order: 1
10 | ---
11 |
12 | # Immer
13 |
14 | **DEPRECATED: This package is depreacted in favor of [the offficial use-immer package](https://github.com/immerjs/use-immer).**
15 |
16 | Provides reducer and state hooks bound to [immer](https://github.com/immerjs/immer) library.
17 |
18 | ```shell
19 | npm install @huse/immer
20 | ```
--------------------------------------------------------------------------------
/packages/methods/src/interface.ts:
--------------------------------------------------------------------------------
1 | /* istanbul ignore file */
2 | export interface NativeReducers {
3 | [key: string]: (state: S, ...args: any[]) => S;
4 | }
5 |
6 | export interface ImmerReducers {
7 | [key: string]: (state: S, ...args: any[]) => S | void;
8 | }
9 |
10 | type Strip | ImmerReducers> = {[K in keyof R]: Strip
23 |
24 |
25 |
--------------------------------------------------------------------------------
/packages/timeout/docs/useStableInterval.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useStableInterval
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Timeout
8 | path: /timeout
9 | order: 4
10 | ---
11 |
12 | # useStableInterval
13 |
14 | Like `useInterval` but counts time ellapsed to execute `callback`, when `callback` returns a `Promise`, it's resolve time is counted.
15 |
16 | `useStableInterval` ensures the next execution of callback is triggered after specified `delay` after `callback` is returned or resolved.
--------------------------------------------------------------------------------
/packages/debug/docs/useRenderTimes.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useRenderTimes
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Debug
8 | path: /debug
9 | order: 2
10 | ---
11 |
12 | # useRenderTimes
13 |
14 | Returns the times of render triggered.
15 |
16 | ```typescript
17 | function useRenderTimes(): number
18 | ```
19 |
20 | This returned times starts from `1` and increase on each render, even props and states are not changed.
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/packages/window-size/docs/demo/useWindowSize.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {useWindowSize} from '@huse/window-size';
3 |
4 | export default () => {
5 | const size = useWindowSize();
6 | return (
7 | <>
8 |
25 |
--------------------------------------------------------------------------------
/packages/router/docs/useNavigate.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useNavigate
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Router
8 | path: /router
9 | order: 2
10 | ---
11 |
12 | # useNavigate
13 |
14 | A implement of `react-router@6.x`'s `useNavigate` above version `5.x`.
15 |
16 | ```typescript
17 | interface NavigateOptions {
18 | replace?: boolean;
19 | state?: S;
20 | }
21 |
22 | type Navigate = (to: Location | string, options?: NavigateOptions) => void;
23 |
24 | function useNavigate(): Navigate;
25 | ```
--------------------------------------------------------------------------------
/packages/click-outside/src/index.ts:
--------------------------------------------------------------------------------
1 | import {RefObject} from 'react';
2 | import {useDocumentEvent} from '@huse/document-event';
3 |
4 | export function useClickOutside(ref: RefObject
--------------------------------------------------------------------------------
/packages/debounce/docs/useDebouncedValue.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useDebouncedValue
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Debounce
8 | path: /debounce
9 | order: 3
10 | ---
11 |
12 | # useDebouncedValue
13 |
14 | Derive a given value and debounce its update by a given delay.
15 |
16 | ```typescript
17 | function useDebouncedValue
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/packages/input-value/src/index.ts:
--------------------------------------------------------------------------------
1 | import {useState, useCallback} from 'react';
2 |
3 | interface ChangeEvent {
4 | target: {value: string};
5 | }
6 |
7 | export interface InputValueState {
8 | value: string;
9 | onChange(e: ChangeEvent): void;
10 | }
11 |
12 | export function useInputValue(initialValue: string = ''): InputValueState {
13 | const [value, setValue] = useState(initialValue);
14 | const onChange = useCallback(
15 | (e: ChangeEvent) => setValue(e.target.value),
16 | []
17 | );
18 | return {value, onChange};
19 | }
20 |
--------------------------------------------------------------------------------
/packages/router/docs/useSearchParamState.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useSearchParamState
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Router
8 | path: /router
9 | order: 8
10 | ---
11 |
12 | # useSearchParamState
13 |
14 | Wrap a single search params as a react state.
15 |
16 | ```typescript
17 | function useSearchParamState(key: string, options?: NavigateOptions): [string | null, (value: string) => void];
18 | ```
19 |
20 | When a state is stored in search params, using this hook works just like `useState`.
21 |
22 |
--------------------------------------------------------------------------------
/packages/update/src/__tests__/index.test.js:
--------------------------------------------------------------------------------
1 | import {renderHook, act} from '@testing-library/react-hooks';
2 | import {useForceUpdate} from '../index';
3 |
4 | test('valid return type', () => {
5 | const {result} = renderHook(() => useForceUpdate());
6 | expect(typeof result.current).toBe('function');
7 | });
8 |
9 | test('will force update', () => {
10 | const init = jest.fn(() => useForceUpdate());
11 | const {result} = renderHook(init);
12 | expect(init).toHaveBeenCalledTimes(1);
13 | act(() => result.current());
14 | expect(init).toHaveBeenCalledTimes(2);
15 | });
16 |
--------------------------------------------------------------------------------
/packages/update/docs/demo/useForceUpdate.tsx:
--------------------------------------------------------------------------------
1 | import React, {useRef} from 'react';
2 | import {Button} from 'antd';
3 | import 'antd/dist/antd.min.css';
4 | import {useForceUpdate} from '@huse/update';
5 |
6 | export default () => {
7 | const renderTimes = useRef(0);
8 | const forceUpdate = useForceUpdate();
9 | renderTimes.current++;
10 | return (
11 |
30 |
--------------------------------------------------------------------------------
/packages/previous-value/docs/useOriginalDeepCopy.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useOriginalDeepCopy
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Previous Value
8 | path: /previous-value
9 | order: 4
10 | ---
11 |
12 | # useOriginalDeepCopy
13 |
14 | This is a short cut version of `useOriginalCopy` using a deep equality function.
15 |
16 | ```typescript
17 | type CustomEquals
--------------------------------------------------------------------------------
/packages/document-title/docs/useDocumentTitle.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useDocumentTitle
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Document Title
8 | path: /document-title
9 | order: 1
10 | ---
11 |
12 | # useDocumentTitle
13 |
14 | This hook updates `document.title` to custom value and will revert the change on component unmount.
15 |
16 | ```typescript
17 | function useDocumentTitle(title: string): void;
18 | ```
19 |
20 | Since it reverts `document.title` to previous value, it should be safe to have multiple components using this hook simultaneously.
21 |
22 |
23 |
--------------------------------------------------------------------------------
/packages/script/demo/components/WithSuspense/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/jsx-no-bind */
2 | import {useState} from 'react';
3 | import {useScriptSuspense} from '../../../src';
4 |
5 | const WithSuspense = () => {
6 | const [index, setIndex] = useState(0);
7 | useScriptSuspense(index === 0 ? undefined : `/${index}.js`);
8 |
9 | return (
10 |
--------------------------------------------------------------------------------
/packages/boolean/docs/useBoolean.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useBoolean
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Boolean
8 | path: /boolean
9 | order: 2
10 | ---
11 |
12 | # useBoolean
13 |
14 | `useBoolean` returns a `[value, methods]` tuple, in which methods are listed as:
15 |
16 | ```typescript
17 | interface BooleanMethods {
18 | // Change value to true
19 | on(): void;
20 | // Change value to false
21 | off(): void;
22 | // Toggle current value, can force update if a boolean argument is provided
23 | toggle(value: unknown): void;
24 | }
25 | ```
26 |
27 |
--------------------------------------------------------------------------------
/packages/previous-value/docs/demo/usePreviousValue.tsx:
--------------------------------------------------------------------------------
1 | import React, {useReducer} from 'react';
2 | import {Button} from 'antd';
3 | import 'antd/dist/antd.min.css';
4 | import {usePreviousValue} from '@huse/previous-value';
5 |
6 | export default () => {
7 | const [value, increment] = useReducer(v => v + 1, 0);
8 | const previousValue = usePreviousValue(value);
9 | return (
10 |
23 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": [
3 | "packages/*/src/**/*.ts",
4 | "packages/*/docs/**/*.ts",
5 | "packages/*/docs/**/*.tsx",
6 | ],
7 | "exclude": [
8 | "packages/**/__tests__/**"
9 | ],
10 | "compilerOptions": {
11 | "jsx": "preserve",
12 | "allowJs": false,
13 | "target": "ES2015",
14 | "module": "CommonJS",
15 | "esModuleInterop": true,
16 | "strict": true,
17 | "noImplicitAny": false,
18 | "sourceMap": true,
19 | "noEmit": false,
20 | "declaration": true,
21 | "skipLibCheck": true,
22 | "moduleResolution": "node"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/packages/scroll-lock/docs/useScrollLock.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useScrollLock
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Scroll Lock
8 | path: /scroll-lock
9 | order: 2
10 | ---
11 |
12 | # useScrollLock
13 |
14 | Pass a `boolean` value to lock scroll on document.
15 |
16 | ```typescript
17 | function useScrollLock(lock: boolean): void
18 | ```
19 |
20 | When `lock` is `true` scroll will be locked, `overflow` style will be reverted when `lock` becomes `false` or component is unmounted.
21 |
22 | If an other modification of `overflow` style happens after scroll lock, `useScrollLock` will not revert the style.
23 |
24 |
25 |
--------------------------------------------------------------------------------
/packages/intended-lazy/src/index.ts:
--------------------------------------------------------------------------------
1 | import {useImperativeHandle, useRef} from 'react';
2 |
3 | export function useIntendedLazyValue(patch: SearchParamsPatch, options?: NavigateOptions) => void;
22 |
23 | function useUpdateSearchParams(): UpdateSearchParams;
24 | ```
25 |
26 | `URLSearchParams#toString` is used to stringify search params to search string.
--------------------------------------------------------------------------------
/packages/scroll-into-view/src/index.ts:
--------------------------------------------------------------------------------
1 | import {useEffect, RefObject, useRef} from 'react';
2 |
3 | export function useScrollIntoView(
4 | ref: RefObjectScroll Your Web 👇
10 |
25 |
--------------------------------------------------------------------------------
/packages/input-value/docs/useInputValue.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useInputValue
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Input Value
8 | path: /input-value
9 | order: 2
10 | ---
11 |
12 | # useInputValue
13 |
14 | To get rid of the duplication of `e => setState(e.target.value)`, `useInputValue` returns an object containing both `value` and change event aware `onChange`.
15 |
16 | ```typescript
17 | interface InputValueState {
18 | value: string;
19 | onChange(e: ChangeEvent
--------------------------------------------------------------------------------
/packages/performance/docs/useLayoutTiming.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useLayoutTiming
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Performance
8 | path: /performance
9 | order: 3
10 | ---
11 |
12 | # useLayoutTiming
13 |
14 | This hooks track a meaningful layout once.
15 |
16 | ```typescript
17 | interface TimeRange {
18 | start: number;
19 | end: number;
20 | ellapsed: number;
21 | }
22 |
23 | function useLayoutTiming(callback: (timing: TimeRange) => void, meaningful?: boolean): void
24 | ```
25 |
26 | `meaningful` is `true` by default in case the first layout will be reported,
27 | you can dynamiclly pass it to record a more meaningful layout time.
28 |
29 |
--------------------------------------------------------------------------------
/packages/poll/docs/demo/usePoll.tsx:
--------------------------------------------------------------------------------
1 | import React, {useCallback} from 'react';
2 | import {Spin} from 'antd';
3 | import 'antd/dist/antd.min.css';
4 | import {usePoll} from '@huse/poll';
5 |
6 | export default () => {
7 | const generateRandom = useCallback(
8 | // Delay for 1.5s and resolve with a random value
9 | () => new Promise(resolve => setTimeout(() => resolve(Math.random()), 1500)),
10 | []
11 | );
12 | // Polls every 10s
13 | const [value, pendingCount] = usePoll(generateRandom, 10 * 1000);
14 | return (
15 |
--------------------------------------------------------------------------------
/packages/debug/docs/demo/useRenderTimes.tsx:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react';
2 | import {Button} from 'antd';
3 | import 'antd/dist/antd.min.css';
4 | import {useRenderTimes, useChangeTimes, useUpdateCause} from '@huse/debug';
5 |
6 | export default () => {
7 | const [value, setValue] = useState(0);
8 | const renderTimes = useRenderTimes();
9 | return (
10 | <>
11 |
12 |
13 |
14 | >
15 | );
16 | };
--------------------------------------------------------------------------------
/docs/docs/packages.en-US.md:
--------------------------------------------------------------------------------
1 | # Project structure
2 |
3 | This is a typical pnpm workspace based monorepo, each hook creates a package in `packages` folder, the folder name is dash-cased and without the `use` prefix.
4 |
5 | Package name must comform a format of `@huse/foo-bar`.
6 |
7 | ```bash
8 |
9 | packages
10 | │ ├── action-pending
11 | │ ├── window-size
12 | │ ├── foo-bar
13 | │ └── xxx-xxx
14 |
15 | ```
16 |
17 | Each package should named export at least one hook like:
18 |
19 | ```js
20 | import { useInputValue } from '@huse/input-value';
21 | ```
22 |
23 | ## Unit Tests
24 | Unit tests are recommended, they are placed inside `src/__tests__` folder with an extension of `.test.js`, we highly recommend a 100% of branch coverage.
25 |
--------------------------------------------------------------------------------
/packages/debug/docs/demo/useChangeTimes.tsx:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react';
2 | import {Button} from 'antd';
3 | import 'antd/dist/antd.min.css';
4 | import {useRenderTimes, useChangeTimes, useUpdateCause} from '@huse/debug';
5 |
6 | export default () => {
7 | const [value, setValue] = useState(0);
8 | const renderTimes = useChangeTimes(value);
9 | return (
10 | <>
11 |
12 |
13 |
14 | >
15 | );
16 | };
--------------------------------------------------------------------------------
/packages/element-size/docs/useElementSize.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useElementSize
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Element Size
8 | path: /element-size
9 | order: 3
10 | ---
11 |
12 | # useElementSize
13 |
14 | Observes and returns the offset size of an element.
15 |
16 |
17 | ```typescript
18 | export interface Size {
19 | width: number; // offsetWidth
20 | height: number; // offsetHeight
21 | }
22 |
23 | type ElementResizeCallback = (element: HTMLElement | null) => void;
24 |
25 | function useElementSize(): [ElementResizeCallback, Size | undefined];
26 | ```
27 |
28 | The initial size is `undefined` and will be updated on mount anytime element is resized.
29 |
30 |
--------------------------------------------------------------------------------
/packages/network/docs/demo/useOnLine.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {CheckCircleOutlined, CloseCircleOutlined} from '@ant-design/icons';
3 | import 'antd/dist/antd.min.css';
4 | import {useOnLine} from '@huse/network';
5 |
6 | export default () => {
7 | const isOnLine = useOnLine();
8 | return (
9 | <>
10 | {isOnLine ?
--------------------------------------------------------------------------------
/packages/optimistic/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: README
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Optimistic
8 | path: /optimistic
9 | order: 1
10 | ---
11 |
12 | # Optimistic
13 |
14 | Provides a set of react hooks to help manage optimistic states.
15 |
16 | As previously stated in [redux-optimistic-thunk](https://github.com/ecomfe/redux-optimistic-thunk#why-this-middleware),
17 | manually managing optimistic states, commits, rollbacks and transactions is not ideal model of state management.
18 | React hooks provide powers to manage states in a more functional way, and this library aimed to build optimistic functions above hooks.
19 |
20 | **This library requires [ES6 Generators](https://caniuse.com/#feat=es6-generators) to work.**
--------------------------------------------------------------------------------
/packages/debounce/docs/demo/useDebouncedValue.tsx:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react';
2 | import { Input } from 'antd';
3 | import 'antd/dist/antd.min.css';
4 | import { useDebouncedValue } from '@huse/debounce';
5 |
6 | export default () => {
7 | const [value, setValue] = useState('');
8 | const debouncedValue = useDebouncedValue(value, 200); // debounced update 200ms
9 | return (
10 | <>
11 |
--------------------------------------------------------------------------------
/packages/immer/docs/useImmerState.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useImmerState
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Immer
8 | path: /immer
9 | order: 2
10 | ---
11 |
12 | # useImmerState
13 |
14 | Like `useState` but give the ability to update state by directly mutate it.
15 |
16 | ```typescript
17 | type ImmerStateProducer = (state: S) => S | void;
18 | type SetImmerState = (next: S | ImmerStateProducer) => void;
19 | type ImmerState = [S, SetImmerState];
20 | function useImmerState(initialState: S | (() => S)): ImmerState;
21 | ```
22 |
23 | This works exactly the same as `useState` with a single difference that when a function is passed to `setState`, it can mutate state directly.
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/packages/intersection/docs/useOnScreenCallback.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useOnScreenCallback
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Intersection
8 | path: /intersection
9 | order: 4
10 | ---
11 |
12 | # useOnScreenCallback
13 |
14 | This is a fundamental hook which triggers a callback when element intersects with screen.
15 |
16 | ```typescript
17 | type OnScreenOptions = Omit
--------------------------------------------------------------------------------
/packages/immer/docs/useImmerReducer.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useImmerReducer
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Immer
8 | path: /immer
9 | order: 3
10 | ---
11 |
12 | # useImmerReducer
13 |
14 | A `useReducer` bound to immer, direct mutation of state is allowed inside reducer.
15 |
16 | ```typescript
17 | type ImmerReducer = (state: S, action: A) => S | void;
18 | function useImmerReducer(reducer: ImmerReducer, initialState: S, initializer?: () => S): [S, Dispatch];
19 | ```
20 |
21 | Some differences with `useReducer`:
22 |
23 | 1. Requires a single `action` argument in reducer.
24 | 2. reducer can mutate state directly.
25 | 3. `initializer` won't receive `initialState` as its argument.
26 |
27 |
--------------------------------------------------------------------------------
/packages/performance/docs/demo/useLayoutTiming.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useReducer } from 'react';
2 | import { Button } from 'antd';
3 | import 'antd/dist/antd.min.css';
4 | import { useLayoutTiming } from '@huse/performance';
5 |
6 | export default () => {
7 | const [timing, setTiming] = useState();
8 | const [userClicked, setClicked] = useReducer(() => true, false);
9 | useLayoutTiming(setTiming, userClicked);
10 | return (
11 | <>
12 |
27 |
--------------------------------------------------------------------------------
/packages/input-value/docs/demo/useInputValue.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Input, Form} from 'antd';
3 | import 'antd/dist/antd.min.css';
4 | import {useInputValue} from '@huse/input-value';
5 |
6 | export default () => {
7 | const name = useInputValue('');
8 | const age = useInputValue(10);
9 | const layout = {
10 | labelCol: { span: 3 },
11 | wrapperCol: { span: 21 },
12 | };
13 | return (
14 | <>
15 |
21 |
22 |
23 | This hook identifies "change" by reference, to inspect why a value has changed, try `useUpdateCause` hook.
24 |
25 | ```javascript
26 | import {useChangeTimes} from '@huse/debug';
27 |
28 | const App = props => {
29 | const renderTimes = useChangeTimes(props.foo);
30 |
31 | console.log(renderTimes);
32 |
33 | return (
34 | // ...
35 | );
36 | };
37 | ```
--------------------------------------------------------------------------------
/packages/boolean/docs/demo/useBoolean.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Button, Switch } from 'antd';
3 | import 'antd/dist/antd.min.css';
4 | import './useBoolean.less';
5 | import { useBoolean } from '@huse/boolean';
6 |
7 | export default () => {
8 | const [value, {on, off, toggle}] = useBoolean();
9 | return (
10 | <>
11 |
22 | );
23 | };
24 |
25 | render(
26 |
28 |
--------------------------------------------------------------------------------
/packages/timeout/docs/demo/useInterval.tsx:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react';
2 | import {Slider} from 'antd';
3 | import 'antd/dist/antd.min.css';
4 | import {useTimeout, useInterval} from '@huse/timeout';
5 |
6 | export default () => {
7 | const [theme, setTheme] = useState('light');
8 | // Switch theme every 5s
9 | useInterval(
10 | () => setTheme(theme => (theme === 'light' ? 'dark' : 'light')),
11 | 5 * 1000
12 | );
13 | const style = {
14 | height: 40,
15 | lineHeight: '40px',
16 | textAlign: 'center',
17 | backgroundColor: theme === 'light' ? '#fff' : '#222',
18 | color: theme === 'light' ? '#666' : '#f4f5f6',
19 | };
20 | return (
21 | <>
22 | (defaultValue: S, defaultDuration?: number)
20 | ```
21 |
22 | The `setValue` takes an extra `duration` argument to specify the time before value change back to default, it defaults to `defaultDuration` argument of `useTransitionState`.
23 |
24 |
25 |
--------------------------------------------------------------------------------
/packages/document-title/docs/demo/useDocumentTitle.tsx:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react';
2 | import { Input,Row,Col } from 'antd';
3 | import 'antd/dist/antd.min.css';
4 | import {useDocumentTitle} from '@huse/document-title';
5 |
6 | export default () => {
7 | const [title, setTitle] = useState('');
8 | useDocumentTitle(title);
9 | return (
10 | <>
11 | change document.title
12 | document.title)
27 |
--------------------------------------------------------------------------------
/packages/debounce/docs/useDebouncedCallback.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useDebouncedCallback
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Debounce
8 | path: /debounce
9 | order: 4
10 | ---
11 |
12 | # useDebouncedCallback
13 |
14 | Simply wrap a callback to a debounced one.
15 |
16 | ```typescript
17 | function useDebouncedCallback
25 |
--------------------------------------------------------------------------------
/packages/scroll-lock/docs/demo/useScrollLock.tsx:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react';
2 | import {Button, Modal} from 'antd';
3 | import 'antd/dist/antd.min.css';
4 | import {useScrollLock} from '@huse/scroll-lock';
5 |
6 | export default () => {
7 | const [modalOpen, setModalOpen] = useState(false);
8 | useScrollLock(modalOpen);
9 | return (
10 | <>
11 |
12 | {
13 | modalOpen && (
14 | Mouse Down:
15 | {down} times
16 | Mouse Up:
19 | {up} times
20 |
22 |
current value: {state.value}
11 |Effect run {effectsCount} times.
24 | 25 |Generated Value: {result.data}
23 | } 24 |
41 |
--------------------------------------------------------------------------------
/packages/hover/docs/useHover.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useHover
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Hover
8 | path: /hover
9 | order: 2
10 | ---
11 |
12 | # useHover
13 |
14 | This hook returns a set of props to handle mouse events in order to report whether element is currently in hover state.
15 |
16 | ```typescript
17 | interface HoverOptions {
18 | delay?: number;
19 | }
20 |
21 | interface HoverCallbacks {
22 | onMouseEnter(event: MouseEvent): void;
23 | onMouseLeave(event: MouseEvent): void;
24 | }
25 |
26 | function useHover(options: HoverOptions = {}): [boolean, HoverCallbacks]
27 | ```
28 |
29 | The `delay` option is default to `0`, with a value less than or equals to 0 hover state will change immediately on mouse enter and leave.
30 |
31 |
32 |
33 | Note that when `delay` is a positive value, `onEnter` and `onLeave` callbacks are also debounced,
34 | once you want these callbacks to execute immediately without debouncing, compose callbacks yourself:
35 |
36 |
37 |
--------------------------------------------------------------------------------
/scripts/generate/prompt.js:
--------------------------------------------------------------------------------
1 | const inquirer = require('inquirer');
2 |
3 | const questions = [
4 | {
5 | type: 'input',
6 | name: 'packageName',
7 | message: 'package name:',
8 | transformer(name) {
9 | return `@huse/${name}`;
10 | },
11 | validate(name) {
12 | return name.length > 1;
13 | },
14 | },
15 | {
16 | type: 'confirm',
17 | name: 'hookTest',
18 | message: 'test with hook support:',
19 | },
20 | {
21 | type: 'confirm',
22 | name: 'domTest',
23 | message: 'test with DOM support:',
24 | },
25 | {
26 | type: 'confirm',
27 | name: 'demo',
28 | message: 'has demo:',
29 | default: false,
30 | },
31 | {
32 | type: 'input',
33 | name: 'hookName',
34 | message: 'initial hook name:',
35 | validate(name) {
36 | return name.length > 3 && name.startsWith('use');
37 | },
38 | },
39 | ];
40 |
41 | module.exports = () => inquirer.prompt(questions);
42 |
--------------------------------------------------------------------------------
/packages/poll/docs/demo/usePoll2.tsx:
--------------------------------------------------------------------------------
1 | import React, {useCallback} from 'react';
2 | import {Spin} from 'antd';
3 | import 'antd/dist/antd.min.css';
4 | import {usePoll} from '@huse/poll';
5 |
6 | export default () => {
7 | const generateRandom = useCallback(
8 | // Delay for 1.5s and resolve with a random value
9 | () => new Promise(resolve => setTimeout(() => resolve(Math.random()), 1500)),
10 | []
11 | );
12 | const options = {
13 | minInterval: 5 * 1000,
14 | maxInterval: 20 * 1000,
15 | maxIdleTime: 30 * 1000,
16 | stopOnInactive: true,
17 | };
18 | const [value, pendingCount] = usePoll(generateRandom, options);
19 | return (
20 | <>
21 |
22 | {value}
23 | {!!pendingCount && }
24 |
25 | Poll will slow down when you keep inactive for 30s.
26 | Poll will stop when you switch to another app.
27 | >
28 | );
29 | };
--------------------------------------------------------------------------------
/packages/update/docs/useForceUpdate.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useForceUpdate
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Update
8 | path: /update
9 | order: 2
10 | ---
11 |
12 | # useForceUpdate
13 |
14 | Return a function which when invoked will update current component.
15 |
16 | ```typescript
17 | function useForceUpdate(): () => void;
18 | ```
19 |
20 | This could have a combination with `useRef` to implement some interesting staff such as a `useState`:
21 |
22 | ```javascript
23 | import {useForceUpdate} from '@huse/update';
24 |
25 | const useRefState = initialValue => {
26 | const state = useRef(initialValue);
27 | const forceUpdate = useForceUpdate();
28 | const setState = useCallback(
29 | value => {
30 | state.current = value;
31 | forceUpdate();
32 | },
33 | [forceUpdate]
34 | );
35 | return [state.current, setState];
36 | };
37 | ```
38 |
39 | Every time `forceUpdate` is invoked, component will have a re-render despite of other state changes or `memo` usage.
40 |
41 |
42 |
--------------------------------------------------------------------------------
/packages/scroll-position/docs/useScrollPosition.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useScrollPosition
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Scroll Position
8 | path: /scroll-position
9 | order: 2
10 | ---
11 |
12 | # useScrollPosition
13 |
14 | Observe and return scroll position of an element or window.
15 |
16 | ```typescript
17 | interface ScrollPosition {
18 | x: number;
19 | y: number;
20 | left: number;
21 | top: number;
22 | scrollLeft: number;
23 | scrollTop: number;
24 | }
25 |
26 | function useScrollPosition(element?: HTMLElement | null): ScrollPosition
27 | ```
28 |
29 | **Note there are different behaviors when `element` is either `null` or `undefined`, commonly `useRef`'s initial value is `null`.**
30 | When `documentElement` is the observed target, simple `useScrollPosition()` without argument.
31 |
32 | In order to satisfy different developers, the returned `ScrollPosition` has a set of different key pairs,
33 | all `x`, `left`, `scrollLeft` have same value while `y`, `top`, `scrollTop` have same value.
34 |
35 |
36 |
--------------------------------------------------------------------------------
/packages/action-pending/src/index.ts:
--------------------------------------------------------------------------------
1 | import {useCallback, useEffect, useRef} from 'react';
2 | import {useCounter} from '@huse/number';
3 |
4 | type AsyncFunction = (...args: any[]) => Promise;
5 |
6 | export function useActionPending(action: A): [A, number] {
7 | const unmounted = useRef(false);
8 | const [pendingCount, {inc, dec}] = useCounter();
9 | const actionWithPending = useCallback(
10 | (...args: Parameters) => {
11 | inc();
12 | const pending = action(...args);
13 | const safeDec = () => {
14 | if (!unmounted.current) {
15 | dec();
16 | }
17 | };
18 | pending.then(safeDec, safeDec);
19 | return pending;
20 | },
21 | [action, dec, inc]
22 | );
23 | useEffect(
24 | () => {
25 | unmounted.current = false;
26 | return () => {
27 | unmounted.current = true;
28 | };
29 | },
30 | []
31 | );
32 | return [actionWithPending as A, pendingCount];
33 | }
34 |
--------------------------------------------------------------------------------
/packages/timeout/docs/demo/useTimeout.tsx:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react';
2 | import {Slider} from 'antd';
3 | import 'antd/dist/antd.min.css';
4 | import {useTimeout, useInterval} from '@huse/timeout';
5 |
6 | export default () => {
7 | const [theme, setTheme] = useState('light');
8 | const [delay, setDelay] = useState(2);
9 | console.log(delay);
10 | useTimeout(
11 | () => {
12 | console.log('timeout');
13 | setTheme(theme => (theme === 'light' ? 'dark' : 'light'));
14 | },
15 | delay * 1000
16 | );
17 | const style = {
18 | height: 40,
19 | lineHeight: '40px',
20 | textAlign: 'center',
21 | backgroundColor: theme === 'light' ? '#fff' : '#222',
22 | color: theme === 'light' ? '#666' : '#f4f5f6',
23 | };
24 | return (
25 | <>
26 |
27 | Choose a delay to switch theme:
28 |
29 |
30 |
31 | Hello World
32 |
33 | >
34 | );
35 | };
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Baidu EFE team
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/performance/docs/usePerformanceTiming.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: usePerformanceTiming
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Performance
8 | path: /performance
9 | order: 2
10 | ---
11 |
12 | # usePerformanceTiming
13 |
14 | This hook tracks component's layout times and report it to a custom callback function.
15 |
16 | ```typescript
17 | interface Timings {
18 | [flag: string]: number;
19 | initialRender: number;
20 | initialLayout: number;
21 | }
22 |
23 | interface TimingOptions {
24 | flags?: {[name: string]: boolean};
25 | }
26 |
27 | function usePerformanceTiming(callback: (timings: Timings) => void, options?: TimingOptions): void
28 | ```
29 |
30 | It will always trigger `callback` on the first layout, consequent triggers depend on `flags` change,
31 | every time when a flag is changed from `false` to `true` the `callback` will be triggered.
32 |
33 | `callback` function receives a `Timings` object containing at least `initialRender` and `initialLayout` properties,
34 | all flags evaluated to `true` also reflects a property in this argument.
35 |
36 |
--------------------------------------------------------------------------------
/packages/transition-state/src/index.ts:
--------------------------------------------------------------------------------
1 | import {useState, useRef, useEffect, useCallback} from 'react';
2 |
3 | export type TransitionStateHook = [
4 | S,
5 | (value: S, duration?: number) => void,
6 | ];
7 |
8 | export function useTransitionState(defaultValue: S, defaultDuration: number = -1): TransitionStateHook {
9 | const [value, setValue] = useState(defaultValue);
10 | const durationRef = useRef(defaultDuration);
11 | useEffect(
12 | () => {
13 | if (value !== defaultValue && durationRef.current >= 0) {
14 | const tick = setTimeout(
15 | () => setValue(defaultValue),
16 | durationRef.current
17 | );
18 |
19 | return () => clearTimeout(tick);
20 | }
21 | },
22 | [defaultValue, value]
23 | );
24 | const setTransition = useCallback(
25 | (value: S, duration: number = defaultDuration) => {
26 | durationRef.current = duration;
27 | setValue(value);
28 | },
29 | [defaultDuration]
30 | );
31 | return [value, setTransition];
32 | }
33 |
--------------------------------------------------------------------------------
/packages/merged-ref/docs/demo/useMergedRef.tsx:
--------------------------------------------------------------------------------
1 | import React, {useState, useCallback, useRef, useEffect} from 'react';
2 | import {useMergedRef} from '@huse/merged-ref';
3 |
4 | export default () => {
5 | const [size, setSize] = useState([0, 0]);
6 | // callback ref
7 | const observeSize = useCallback(
8 | element => element && setSize([element.offsetWidth, element.offsetHeight]),
9 | []
10 | );
11 | // mutation ref
12 | const elementRef = useRef(null);
13 | useEffect(
14 | () => {
15 | if (elementRef.current) {
16 | elementRef.current.animate(
17 | [{opacity: '1'}, {opacity: '.4'}, {opacity: '1'}],
18 | {duration: 800, iterations: 4}
19 | );
20 | }
21 | },
22 | []
23 | );
24 | const ref = useMergedRef([observeSize, elementRef]);
25 | return (
26 |
30 | {size[0]} x {size[1]}
31 |
32 | );
33 | };
--------------------------------------------------------------------------------
/packages/script/demo/entries/index.js:
--------------------------------------------------------------------------------
1 | import {useState, useEffect, Suspense} from 'react';
2 | import {render} from 'react-dom';
3 | import SimpleScript from '@/components/SimpleScript';
4 | import WithSuspense from '@/components/WithSuspense';
5 |
6 | const App = () => {
7 | const [json, setJSON] = useState('');
8 | useEffect(
9 | () => {
10 | const tick = setInterval(
11 | () => setJSON(JSON.stringify(window.dynamicScripts, null, ' ')),
12 | 500
13 | );
14 | return () => clearInterval(tick);
15 | },
16 | []
17 | );
18 |
19 | return (
20 |
21 | Simple Use Script
22 |
23 | Use With Suspense
24 | Currently Loading...}>
25 |
26 |
27 | Content Loaded:
28 |
29 | {json}
30 |
31 |
32 | );
33 | };
34 |
35 | render(
36 | ,
37 | document.body.appendChild(document.createElement('div'))
38 | );
39 |
--------------------------------------------------------------------------------
/docs/index.zh-CN.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "huse - React Hooks Library"
3 | hero:
4 | title: "huse"
5 | desc: 这是收集了常用的react hooks,用于支持百度内部的开发,我们欢迎社区所有人参与共建
6 | actions:
7 | - text: 快速上手
8 | link: /zh-CN/docs/getting-started
9 | features:
10 | - icon: https://gw.alipayobjects.com/zos/bmw-prod/813f5ed9-6bc4-43d4-9f74-ec81ecf35733/k7htg6n4_w144_h144.png
11 | title: 内容丰富
12 | desc: 拥有丰富的自定义 Hooks,每个 Hooks 都有丰富的在线示例供您体验。
13 | - icon: https://gw.alipayobjects.com/zos/bmw-prod/7659205c-6637-4fa2-8529-d32e5818304b/k7htflfb_w144_h144.png
14 | title: 完备的教程
15 | desc: 适合全年龄段的丰富的 React Hooks 教程,想学 React Hooks,来这里就够了。
16 | - icon: https://gw.alipayobjects.com/zos/bmw-prod/6319a122-e8b8-497f-9b45-37cfbe77edaa/k7htfx7t_w144_h144.png
17 | title: 生产可用
18 | desc: 已经过百度大量的线上系统的考验及打磨,健壮性值得信赖。
19 | footer: Open-source MIT Licensed | Copyright © 2020
Powered by [Baidu EFE team](https://ecomfe.github.io/)
20 | ---
21 |
22 | ## 轻松上手
23 |
24 | 安装依赖:
25 |
26 | ```bash
27 | npm install huse --save
28 | ```
29 |
30 | 也可以使用 `yarn`:
31 |
32 | ```bash
33 | yarn add huse
34 | ```
35 |
36 | 使用 Hooks
37 |
38 | ```bash
39 | import { useBoolean } from 'huse';
40 | ```
41 |
--------------------------------------------------------------------------------
/packages/collection/docs/useArray.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useArray
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Collection
8 | path: /collection
9 | order: 2
10 | ---
11 |
12 | # useArray
13 |
14 | Encapsulate arrays into methods via `useMethods`, contains methods below:
15 |
16 | ```typescript
17 | {
18 | push(item: T): void;
19 | unshift(item: T): void;
20 | pop(): void;
21 | shift(): void;
22 | slice(start?: number, end?: number): void;
23 | splice(index: number, deleteCount: number, ...insertions: T[]): void;
24 | remove(item: T): void;
25 | removeAt(index: number): void;
26 | insertAt(index: number, item: T): void;
27 | concat(item: T | T[]): void;
28 | replace(from: T, to: T): void;
29 | replaceAll(from: T, to: T): void;
30 | replaceAt(index: number, item: T): void;
31 | filter(predicate: (item: T, index: number) => boolean): void;
32 | union(array: T[]): void;
33 | intersect(array: T[]): void;
34 | difference(array: T[]): void;
35 | reverse(): void;
36 | sort(compare?: (x: T, y: T) => number): void;
37 | clear(): void;
38 | }
39 | ```
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/packages/optimistic/docs/useOptimisticTask.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useOptimisticTask
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Optimistic
8 | path: /optimistic
9 | order: 4
10 | ---
11 |
12 | # useOptimisticTask
13 |
14 | This is a binding of `useOptimisticState` and an async task.
15 |
16 | ```typescript
17 | function useOptimisticTask(task: (arg: A) => Promise, optimisticTask: (arg: A) => S, initialState: S);
18 | ```
19 |
20 | - The `task` is an async function `(arg: TArg) => Promise`.
21 | - The `optimisticTask` is a sync version of task provides an optimistic response `(arg: TArg) => TState`.
22 | - Returned `run` function receives the same argument as `task`.
23 |
24 | ```js
25 | const newTodo = async todo => {
26 | const newTodo = await saveTodo(todo);
27 | // We recommend to use a reducer since it is asynchronous
28 | return todos => [...todos, {...newTodo, pending: false, deleted: false}];
29 | };
30 | const optimisticNewTodo = todo => todos => [...todos, {...todo, pending: true, deleted: false}];
31 | const [todos, addTodo] = useOptimisticTask(newTodo, optimisticNewTodo, []);
32 | ```
33 |
34 | `useOptimisticTask` is useful when encapsulating business aware hooks.
--------------------------------------------------------------------------------
/packages/user-media/docs/useUserMedia.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useUserMedia
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: User Media
8 | path: /user-media
9 | order: 2
10 | ---
11 |
12 | # useUserMedia
13 |
14 | This hook tries to open a media stream via its `constraints` argument, returning a context indicating current streaming state, see [getUserMedia](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia) to understand its underlying browser capabilities.
15 |
16 | ```typescript
17 | interface UserMediaHook {
18 | stream: MediaStream | null;
19 | recording: boolean;
20 | error: Error | null;
21 | start: () => void;
22 | stop: () => void;
23 | }
24 |
25 | function useUserMedia(
26 | constraints?: MediaStreamConstraints,
27 | onSuccess?: (stream: MediaStream) => void,
28 | onError?: (error: Error) => void
29 | ): UserMediaHook;
30 | ```
31 |
32 | By default `useUserMedia` requires both video and audio channels.
33 |
34 | **NOTE: On a browser where `getUserMedia` is not implemented, this hook returns `UserMediaHook` object with a special `error` containing `code` property of `"ERR_METHOD_NOT_IMPLEMENTED"`.**
35 |
36 |
37 |
--------------------------------------------------------------------------------
/packages/scroll-into-view/docs/useScrollIntoView.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useScrollIntoView
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Scroll Into View
8 | path: /scroll-into-view
9 | order: 2
10 | ---
11 |
12 | # useScrollIntoView
13 |
14 | To make an active element scroll into view on its mount.
15 |
16 | ```typescript
17 | function useScrollIntoView(
18 | ref: RefObject,
19 | active: boolean = true,
20 | options: boolean | ScrollIntoViewOptions = {behavior: 'smooth'}
21 | ): void
22 | ```
23 |
24 | This hook conforms to `scrollIntoView` function on `HTMLElement`, the `active` argument controls whether scroll should be performed.
25 |
26 | It is recommended to have only one active element performing scroll.
27 |
28 | ```javascript
29 | const App = () => {
30 | const [activeIndex, setActiveIndex] = useState(0);
31 |
32 | return (
33 |
34 |
35 | {colors.map((c, i) => setActiveIndex(i)} />)}
36 |
37 | {colors.map((c, i) => )}
38 |
39 | );
40 | };
41 | ```
42 |
--------------------------------------------------------------------------------
/packages/snapshot/docs/useSnapshotState.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useSnapshotState
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Snapshot
8 | path: /snapshot
9 | order: 2
10 | ---
11 |
12 | # useSnapshotState
13 |
14 | Creates a state with version history and provides `undo` and `redo` functions to traverse the history.
15 |
16 | ```typescript
17 | export interface SnapshotOptions {
18 | // debounce time to commit value to history, defaults to no debounce
19 | delay?: number;
20 | // max history length, defaults to Infinity
21 | limit?: number;
22 | }
23 |
24 | interface Snapshot {
25 | backLength: number;
26 | forwardLength: number;
27 | canUndo: boolean;
28 | canRedo: boolean;
29 | undo(): void;
30 | redo(): void;
31 | }
32 |
33 | export type SnapshotHook = [T, Dispatch>, Snapshot];
34 |
35 | function useSnapshotState(init: T | (() => T), options: SnapshotOptions = {}): SnapshotHook
36 | ```
37 |
38 |
39 |
40 | By passing a `delay` option `useSnapshotState` will behave as debounced,
41 | that is only commit value to history when it is not changed after a certain time.
42 |
43 |
44 |
--------------------------------------------------------------------------------
/packages/scroll-lock/src/__tests__/index.test.js:
--------------------------------------------------------------------------------
1 | import {renderHook} from '@testing-library/react-hooks';
2 | import {useScrollLock} from '../index';
3 |
4 | test('scroll lock', () => {
5 | renderHook(() => useScrollLock(true));
6 | expect(document.body.style.overflow).toBe('hidden');
7 | });
8 |
9 | test('no scroll lock', () => {
10 | renderHook(() => useScrollLock(false));
11 | expect(document.body.style.overflow).toBe('');
12 | });
13 |
14 | test('free lock on unmount', () => {
15 | const {unmount} = renderHook(() => useScrollLock(false));
16 | unmount();
17 | expect(document.body.style.overflow).toBe('');
18 | });
19 |
20 | test('return previous overflow', () => {
21 | document.body.style.overflow = 'visible';
22 | const {unmount} = renderHook(() => useScrollLock(false));
23 | unmount();
24 | expect(document.body.style.overflow).toBe('visible');
25 | document.body.style.overflow = '';
26 | });
27 |
28 | test('no revert when overflow is modified', () => {
29 | const {unmount} = renderHook(() => useScrollLock(false));
30 | document.body.style.overflow = 'visible';
31 | unmount();
32 | expect(document.body.style.overflow).toBe('visible');
33 | document.body.style.overflow = '';
34 | });
35 |
--------------------------------------------------------------------------------
/packages/optimistic/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@huse/optimistic",
3 | "version": "0.10.1",
4 | "keywords": [
5 | "react",
6 | "hooks"
7 | ],
8 | "homepage": "https://github.com/ecomfe/react-hooks/tree/master/packages/optimistic",
9 | "bugs": {
10 | "url": "https://github.com/ecomfe/react-hooks/issues"
11 | },
12 | "license": "MIT",
13 | "main": "cjs/index.js",
14 | "module": "es/index.js",
15 | "types": "es/index.d.ts",
16 | "files": [
17 | "cjs",
18 | "es",
19 | "src"
20 | ],
21 | "scripts": {
22 | "build": "rm -rf es cjs && tsc & tsc --module ESNext --outDir ./es",
23 | "build-check": "tsc",
24 | "lint": "skr lint --strict src demo",
25 | "test": "skr test --coverage"
26 | },
27 | "devDependencies": {
28 | "@reskript/cli": "^1.10.1",
29 | "@reskript/cli-lint": "^1.10.1",
30 | "@reskript/cli-test": "^1.10.1",
31 | "@reskript/config-lint": "^1.10.1",
32 | "@testing-library/react-hooks": "^7.0.1",
33 | "@types/react": "^17.0.14",
34 | "react": "^17.0.0",
35 | "typescript": "^4.3.5"
36 | },
37 | "peerDependencies": {
38 | "react": ">=16.8.0"
39 | },
40 | "publishConfig": {
41 | "access": "public",
42 | "registry": "https://registry.npmjs.com"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/packages/derived-state/src/__tests__/index.test.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/jsx-no-bind */
2 | import {renderHook, act} from '@testing-library/react-hooks';
3 | import {useDerivedState} from '../index';
4 |
5 | test('initial value', () => {
6 | const {result} = renderHook(() => useDerivedState(123));
7 | expect(result.current[0]).toBe(123);
8 | expect(typeof result.current[1]).toBe('function');
9 | });
10 |
11 | test('set state', () => {
12 | const {result} = renderHook(() => useDerivedState(123));
13 | act(() => result.current[1](456));
14 | expect(result.current[0]).toBe(456);
15 | });
16 |
17 | test('custom derive', () => {
18 | const derive = jest.fn(x => x + 1);
19 | const {result} = renderHook(() => useDerivedState(123, derive), {initialProps: {x: 123}});
20 | expect(result.current[0]).toBe(124);
21 | expect(derive).toHaveBeenCalledWith(123, undefined);
22 | });
23 |
24 | test('update derive', () => {
25 | const derive = jest.fn(x => x + 1);
26 | const {result, rerender} = renderHook(props => useDerivedState(props.x, derive), {initialProps: {x: 123}});
27 | act(() => result.current[1](345));
28 | rerender({x: 456});
29 | expect(result.current[0]).toBe(457);
30 | expect(derive).toHaveBeenCalledWith(456, 345);
31 | });
32 |
--------------------------------------------------------------------------------
/packages/effect-ref/docs/demo/useEffectRef.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useCallback, createElement } from "react";
2 | import { Select } from "antd";
3 | import "antd/dist/antd.min.css";
4 | import "./useEffectRef.less";
5 | import { useEffectRef } from "@huse/effect-ref";
6 |
7 | export default () => {
8 | const [tag, setTag] = useState("div");
9 | const [message, setMessage] = useState("");
10 | const updateMessage = useCallback(
11 | (element) =>
12 | setMessage(
13 | `Root element is changed to <${element.nodeName.toLowerCase()}>`
14 | ),
15 | []
16 | );
17 | const ref = useEffectRef(updateMessage);
18 | return (
19 |
20 |
26 | {createElement(tag, { ref }, {message}
)}
27 |
28 | );
29 | };
30 |
--------------------------------------------------------------------------------
/packages/effect-ref/docs/useEffectRef.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useEffectRef
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Effect Ref
8 | path: /effect-ref
9 | order: 2
10 | ---
11 |
12 | # useEffectRef
13 |
14 | This hook returns a callback function, pass it as `ref` prop to any DOM element to run callback on element mount.
15 |
16 | Callback can return a clean-up function like `useEffect`'s callback to clean up side effects.
17 |
18 | A native callback ref may receive element argument as `null`, however `useEffectRef` handles this case internally,
19 | only `HTMLElement` node is passed to callback.
20 |
21 | ```typescript
22 | export type EffectRef = (element: E | null) => void;
23 |
24 | export type RefCallback = (element: E) => (() => void) | void;
25 |
26 | export function useEffectRef(callback: RefCallback): EffectRef;
27 | ```
28 |
29 | Unlike `useRef` which is not responsive to element change, this hook provides ability to observe any element's mount and unmount.
30 |
31 | In case you need to use multiple callback refs on the same DOM element, `useMergedRef` from `@huse/merged-ref` may help.
32 |
33 |
34 |
--------------------------------------------------------------------------------
/packages/intended-lazy/docs/useIntendedLazyValue.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useIntendedLazyValue
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Intended Lazy
8 | path: /intended-lazy
9 | order: 1
10 | ---
11 |
12 | # useIntendedLazyValue
13 |
14 | Wrap a value to a lazy evaluated function, this function is reference stable so that it won't trigger re-render to a component decorated with `memo`.
15 |
16 | **Note: The value is updated after commit phase, this means it's UNSAFE to read value in comonent's `render` and `useLayoutEffect` as well as its children's corresponding lifecycles.**
17 |
18 | ```typescript
19 | function useIntendedLazyValue(value: T): () => T
20 | ```
21 |
22 | In most cases, this is a performance trick to reduce child component's re-renders when this value is used in a event callback triggered by user, by ensuering event callback is always invoked after DOM is updated, the value is almost always in sync.
23 |
24 | We intentionally naming this hook in this way to warn any users about its unsafety in practice, the React team is working on a better solution, till then we will deprecate this hook, a long name can help you to find out all references and replacement with the official solution.
25 |
26 |
27 |
--------------------------------------------------------------------------------
/packages/performance/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@huse/performance",
3 | "version": "1.1.1",
4 | "keywords": [
5 | "react",
6 | "hooks"
7 | ],
8 | "homepage": "https://github.com/ecomfe/react-hooks/tree/master/packages/performance",
9 | "bugs": {
10 | "url": "https://github.com/ecomfe/react-hooks/issues"
11 | },
12 | "license": "MIT",
13 | "main": "cjs/index.js",
14 | "module": "es/index.js",
15 | "types": "es/index.d.ts",
16 | "files": [
17 | "cjs",
18 | "es",
19 | "src"
20 | ],
21 | "scripts": {
22 | "build": "rm -rf es cjs && tsc & tsc --module ESNext --outDir ./es",
23 | "build-check": "tsc",
24 | "lint": "skr lint --strict src demo",
25 | "test": "skr test --coverage --target=react"
26 | },
27 | "devDependencies": {
28 | "@reskript/cli": "^1.10.1",
29 | "@reskript/cli-lint": "^1.10.1",
30 | "@reskript/cli-test": "^1.10.1",
31 | "@reskript/config-lint": "^1.10.1",
32 | "@testing-library/react-hooks": "^7.0.1",
33 | "@types/react": "^17.0.14",
34 | "antd": "^4.16.8",
35 | "react": "^17.0.0",
36 | "typescript": "^4.3.5"
37 | },
38 | "peerDependencies": {
39 | "react": ">=16.8.0"
40 | },
41 | "publishConfig": {
42 | "access": "public",
43 | "registry": "https://registry.npmjs.com"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/packages/transition-state/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@huse/transition-state",
3 | "version": "1.1.1",
4 | "keywords": [
5 | "react",
6 | "hooks"
7 | ],
8 | "homepage": "https://github.com/ecomfe/react-hooks/tree/master/packages/transition-state",
9 | "bugs": {
10 | "url": "https://github.com/ecomfe/react-hooks/issues"
11 | },
12 | "license": "MIT",
13 | "main": "cjs/index.js",
14 | "module": "es/index.js",
15 | "types": "es/index.d.ts",
16 | "files": [
17 | "cjs",
18 | "es",
19 | "src"
20 | ],
21 | "scripts": {
22 | "build": "rm -rf es cjs && tsc & tsc --module ESNext --outDir ./es",
23 | "build-check": "tsc",
24 | "lint": "skr lint --strict src demo",
25 | "test": "skr test --coverage"
26 | },
27 | "devDependencies": {
28 | "@reskript/cli": "^1.10.1",
29 | "@reskript/cli-lint": "^1.10.1",
30 | "@reskript/cli-test": "^1.10.1",
31 | "@reskript/config-lint": "^1.10.1",
32 | "@testing-library/react-hooks": "^7.0.1",
33 | "@types/react": "^17.0.14",
34 | "antd": "^4.16.8",
35 | "react": "^17.0.0",
36 | "typescript": "^4.3.5"
37 | },
38 | "peerDependencies": {
39 | "react": ">=16.8.0"
40 | },
41 | "publishConfig": {
42 | "access": "public",
43 | "registry": "https://registry.npmjs.com"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/packages/media/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@huse/media",
3 | "version": "1.1.1",
4 | "keywords": [
5 | "react",
6 | "hooks"
7 | ],
8 | "homepage": "https://github.com/ecomfe/react-hooks/tree/master/packages/media",
9 | "bugs": {
10 | "url": "https://github.com/ecomfe/react-hooks/issues"
11 | },
12 | "license": "MIT",
13 | "main": "cjs/index.js",
14 | "module": "es/index.js",
15 | "types": "es/index.d.ts",
16 | "files": [
17 | "cjs",
18 | "es",
19 | "src"
20 | ],
21 | "scripts": {
22 | "build": "rm -rf es cjs && tsc & tsc --module ESNext --outDir ./es",
23 | "build-check": "tsc",
24 | "lint": "skr lint --strict src demo",
25 | "start": "skr dev --src=demo",
26 | "test": "echo 'no test in @huse/media'"
27 | },
28 | "devDependencies": {
29 | "@reskript/cli": "^1.10.1",
30 | "@reskript/cli-dev": "^1.10.1",
31 | "@reskript/cli-lint": "^1.10.1",
32 | "@reskript/config-lint": "^1.10.1",
33 | "@types/react": "^17.0.14",
34 | "classnames": "^2.2.6",
35 | "react": "^17.0.0",
36 | "typescript": "^4.3.5",
37 | "webpack": "^5.45.1"
38 | },
39 | "peerDependencies": {
40 | "react": ">=16.8.0"
41 | },
42 | "publishConfig": {
43 | "access": "public",
44 | "registry": "https://registry.npmjs.com"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/packages/poll/demo/entries/index.js:
--------------------------------------------------------------------------------
1 | import {useRef, useCallback} from 'react';
2 | import {render} from 'react-dom';
3 | import {usePoll} from '../../src';
4 |
5 | const options = {
6 | minInterval: 5 * 1000,
7 | maxInterval: 20 * 1000,
8 | stopOnInactive: true,
9 | maxIdleTime: 30 * 1000,
10 | };
11 |
12 | const App = () => {
13 | const requestTime = useRef(new Date().toLocaleString());
14 | const responseTime = useRef(new Date().toLocaleString());
15 | const fetch = useCallback(
16 | async () => {
17 | requestTime.current = new Date().toLocaleString();
18 | await new Promise(resolve => setTimeout(resolve, 1200));
19 | responseTime.current = new Date().toLocaleString();
20 | return Math.random();
21 | },
22 | []
23 | );
24 | const [value, pendingCount] = usePoll(fetch, options);
25 |
26 | return (
27 |
28 | Request Start At: {requestTime.current}
29 | Response Arrived At: {responseTime.current}
30 | Random Value: {value}
31 | Current State: {pendingCount ? 'Fetching' : 'Waiting'}
32 |
33 | );
34 | };
35 |
36 | render(
37 | ,
38 | document.body.appendChild(document.createElement('div'))
39 | );
40 |
--------------------------------------------------------------------------------
/packages/router/docs/demo/useSearchParamState.tsx:
--------------------------------------------------------------------------------
1 | import React, {useCallback} from 'react';
2 | import {Checkbox, Button} from 'antd';
3 | import 'antd/dist/antd.min.css';
4 | import {MemoryRouter, useLocation} from 'react-router-dom';
5 | import {useLocationState, useSearchParamState} from '@huse/router';
6 |
7 | export default () => {
8 | const App = () => {
9 | const location = useLocation();
10 | const [page, setPage] = useSearchParamState('page');
11 | const pageIndex = parseInt(page || '0', 10);
12 | const nextPage = useCallback(
13 | () => setPage((pageIndex + 1).toString()),
14 | [pageIndex, setPage]
15 | );
16 | const start = pageIndex * 10 + 1;
17 | const items = Array.from({length: 10}, (v, i) => ({id: start + i, name: `Item ${start + i}`}));
18 | return (
19 | <>
20 | {location.pathname}{location.search}
21 |
22 | {items.map(i => - {i.name}
)}
23 |
24 |
25 | >
26 | );
27 | };
28 | return (
29 |
30 |
31 |
32 | );
33 | };
--------------------------------------------------------------------------------
/packages/window-size/src/index.ts:
--------------------------------------------------------------------------------
1 | import {useState, useEffect} from 'react';
2 |
3 | export interface WindowSize {
4 | innerWidth: number;
5 | innerHeight: number;
6 | outerWidth: number;
7 | outerHeight: number;
8 | }
9 |
10 | const getWindowSize = (): WindowSize => {
11 | /* istanbul ignore next */
12 | if (typeof window === 'undefined') {
13 | return {
14 | innerWidth: 0,
15 | innerHeight: 0,
16 | outerWidth: 0,
17 | outerHeight: 0,
18 | };
19 | }
20 | return {
21 | innerWidth: window.innerWidth,
22 | innerHeight: window.innerHeight,
23 | outerWidth: window.outerWidth,
24 | outerHeight: window.outerHeight,
25 | };
26 | };
27 |
28 | export function useWindowSize(): WindowSize {
29 | const [size, setSize] = useState(getWindowSize());
30 | useEffect(
31 | () => {
32 | /* istanbul ignore else */
33 | if (typeof window !== 'undefined') {
34 | const updateWindowSize = () => setSize(getWindowSize());
35 | window.addEventListener('resize', updateWindowSize);
36 | return () => window.removeEventListener('resize', updateWindowSize);
37 | }
38 | },
39 | []
40 | );
41 | return size;
42 | }
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | # next.js build output
61 | .next
62 |
63 | # reskript build assets
64 | /*/*/es
65 | /*/*/cjs
66 | packages/doc/.docz
67 | .DS_Store
68 | /.umi
69 | /.umi-production
70 | /dist
71 |
--------------------------------------------------------------------------------
/packages/timeout/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@huse/timeout",
3 | "version": "1.1.1",
4 | "keywords": [
5 | "react",
6 | "hooks"
7 | ],
8 | "homepage": "https://github.com/ecomfe/react-hooks/tree/master/packages/timeout",
9 | "bugs": {
10 | "url": "https://github.com/ecomfe/react-hooks/issues"
11 | },
12 | "license": "MIT",
13 | "main": "cjs/index.js",
14 | "module": "es/index.js",
15 | "types": "es/index.d.ts",
16 | "files": [
17 | "cjs",
18 | "es",
19 | "src"
20 | ],
21 | "scripts": {
22 | "build": "rm -rf es cjs && tsc & tsc --module ESNext --outDir ./es",
23 | "build-check": "tsc",
24 | "lint": "skr lint --strict src demo",
25 | "test": "skr test --coverage"
26 | },
27 | "devDependencies": {
28 | "@reskript/cli": "^1.10.1",
29 | "@reskript/cli-lint": "^1.10.1",
30 | "@reskript/cli-test": "^1.10.1",
31 | "@reskript/config-lint": "^1.10.1",
32 | "@testing-library/react-hooks": "^7.0.1",
33 | "@types/react": "^17.0.14",
34 | "antd": "^4.16.8",
35 | "react": "^17.0.0",
36 | "react-test-renderer": "^17.0.0",
37 | "typescript": "^4.3.5"
38 | },
39 | "peerDependencies": {
40 | "react": ">=16.8.0"
41 | },
42 | "publishConfig": {
43 | "access": "public",
44 | "registry": "https://registry.npmjs.com"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/packages/hover/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@huse/hover",
3 | "version": "1.1.2",
4 | "keywords": [
5 | "react",
6 | "hooks"
7 | ],
8 | "homepage": "https://github.com/ecomfe/react-hooks/tree/master/packages/hover",
9 | "bugs": {
10 | "url": "https://github.com/ecomfe/react-hooks/issues"
11 | },
12 | "license": "MIT",
13 | "main": "cjs/index.js",
14 | "module": "es/index.js",
15 | "types": "es/index.d.ts",
16 | "files": [
17 | "cjs",
18 | "es",
19 | "src"
20 | ],
21 | "scripts": {
22 | "build": "rm -rf es cjs && tsc & tsc --module ESNext --outDir ./es",
23 | "build-check": "tsc",
24 | "lint": "skr lint --strict src demo",
25 | "test": "skr test --coverage --target=react"
26 | },
27 | "dependencies": {
28 | "@huse/boolean": "^1.1.2"
29 | },
30 | "devDependencies": {
31 | "@reskript/cli": "^1.10.1",
32 | "@reskript/cli-lint": "^1.10.1",
33 | "@reskript/cli-test": "^1.10.1",
34 | "@reskript/config-lint": "^1.10.1",
35 | "@testing-library/react-hooks": "^7.0.1",
36 | "@types/react": "^17.0.14",
37 | "react": "^17.0.0",
38 | "typescript": "^4.3.5"
39 | },
40 | "peerDependencies": {
41 | "react": ">=16.8.0"
42 | },
43 | "publishConfig": {
44 | "access": "public",
45 | "registry": "https://registry.npmjs.com"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/packages/request/docs/useRequestCallback.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useRequestCallback
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Request
8 | path: /request
9 | order: 3
10 | ---
11 |
12 | # useRequestCallback
13 |
14 | Like `useReqeust` but instead of trigger request automatically this hook returns a function (`() => void`) to allow manual trigger of request.
15 |
16 | ```typescript
17 | function useRequestCallback(task: Request, params: K, options?: RequestOptions): [() => void, RequestResult]
18 | ```
19 |
20 | This allows to attach request with user interaction like button clicks.
21 |
22 | ```javascript
23 | import {Spin} from 'antd';
24 | import {noop} from 'lodash';
25 | import {useRequestCallback} from '@huse/request';
26 |
27 | const api = id => fetch(`/users/${id}`).then(r => r.json());
28 |
29 | const App = ({user}) => {
30 | const [request, result] = useRequestCallback(api, {id: user.id});
31 |
32 | if (result.pending || !result.data) {
33 | return (
34 |
35 | show detail
36 | {!!result.pending && }
37 |
38 | );
39 | }
40 |
41 | return (
42 |
43 | username: {result.data.username}
44 |
45 | );
46 | };
--------------------------------------------------------------------------------
/packages/input-value/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@huse/input-value",
3 | "version": "1.1.1",
4 | "keywords": [
5 | "react",
6 | "hooks"
7 | ],
8 | "homepage": "https://github.com/ecomfe/react-hooks/tree/master/packages/input-value",
9 | "bugs": {
10 | "url": "https://github.com/ecomfe/react-hooks/issues"
11 | },
12 | "license": "MIT",
13 | "main": "cjs/index.js",
14 | "module": "es/index.js",
15 | "types": "es/index.d.ts",
16 | "files": [
17 | "cjs",
18 | "es",
19 | "src"
20 | ],
21 | "scripts": {
22 | "build": "rm -rf es cjs && tsc & tsc --module ESNext --outDir ./es",
23 | "build-check": "tsc",
24 | "lint": "skr lint --strict src demo",
25 | "test": "skr test --coverage"
26 | },
27 | "devDependencies": {
28 | "@reskript/cli": "^1.10.1",
29 | "@reskript/cli-lint": "^1.10.1",
30 | "@reskript/cli-test": "^1.10.1",
31 | "@reskript/config-lint": "^1.10.1",
32 | "@testing-library/react-hooks": "^7.0.1",
33 | "@types/react": "^17.0.14",
34 | "antd": "^4.16.8",
35 | "react": "^17.0.0",
36 | "react-test-renderer": "^17.0.0",
37 | "typescript": "^4.3.5"
38 | },
39 | "peerDependencies": {
40 | "react": ">=16.8.0"
41 | },
42 | "publishConfig": {
43 | "access": "public",
44 | "registry": "https://registry.npmjs.com"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/packages/scroll-lock/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@huse/scroll-lock",
3 | "version": "1.1.1",
4 | "keywords": [
5 | "react",
6 | "hooks"
7 | ],
8 | "homepage": "https://github.com/ecomfe/react-hooks/tree/master/packages/scroll-lock",
9 | "bugs": {
10 | "url": "https://github.com/ecomfe/react-hooks/issues"
11 | },
12 | "license": "MIT",
13 | "main": "cjs/index.js",
14 | "module": "es/index.js",
15 | "types": "es/index.d.ts",
16 | "files": [
17 | "cjs",
18 | "es",
19 | "src"
20 | ],
21 | "scripts": {
22 | "build": "rm -rf es cjs && tsc & tsc --module ESNext --outDir ./es",
23 | "build-check": "tsc",
24 | "lint": "skr lint --strict src demo",
25 | "test": "skr test --coverage --target=react"
26 | },
27 | "devDependencies": {
28 | "@reskript/cli": "^1.10.1",
29 | "@reskript/cli-lint": "^1.10.1",
30 | "@reskript/cli-test": "^1.10.1",
31 | "@reskript/config-lint": "^1.10.1",
32 | "@testing-library/react-hooks": "^7.0.1",
33 | "@types/react": "^17.0.14",
34 | "antd": "^4.16.8",
35 | "enzyme": "^3.11.0",
36 | "react": "^17.0.0",
37 | "typescript": "^4.3.5"
38 | },
39 | "peerDependencies": {
40 | "react": ">=16.8.0"
41 | },
42 | "publishConfig": {
43 | "access": "public",
44 | "registry": "https://registry.npmjs.com"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/packages/derived-state/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@huse/derived-state",
3 | "version": "1.1.1",
4 | "keywords": [
5 | "react",
6 | "hooks"
7 | ],
8 | "homepage": "https://github.com/ecomfe/react-hooks/tree/master/packages/derived-state",
9 | "bugs": {
10 | "url": "https://github.com/ecomfe/react-hooks/issues"
11 | },
12 | "license": "MIT",
13 | "main": "cjs/index.js",
14 | "module": "es/index.js",
15 | "types": "es/index.d.ts",
16 | "files": [
17 | "cjs",
18 | "es",
19 | "src"
20 | ],
21 | "scripts": {
22 | "build": "rm -rf es cjs && tsc & tsc --module ESNext --outDir ./es",
23 | "build-check": "tsc",
24 | "lint": "skr lint --strict src demo",
25 | "test": "skr test --coverage"
26 | },
27 | "devDependencies": {
28 | "@reskript/cli": "^1.10.1",
29 | "@reskript/cli-lint": "^1.10.1",
30 | "@reskript/cli-test": "^1.10.1",
31 | "@reskript/config-lint": "^1.10.1",
32 | "@testing-library/react-hooks": "^7.0.1",
33 | "@types/react": "^17.0.14",
34 | "antd": "^4.16.8",
35 | "react": "^17.0.0",
36 | "react-test-renderer": "^17.0.0",
37 | "typescript": "^4.3.5"
38 | },
39 | "peerDependencies": {
40 | "react": ">=16.8.0"
41 | },
42 | "publishConfig": {
43 | "access": "public",
44 | "registry": "https://registry.npmjs.com"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/packages/intended-lazy/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@huse/intended-lazy",
3 | "version": "1.1.2",
4 | "keywords": [
5 | "react",
6 | "hooks"
7 | ],
8 | "homepage": "https://github.com/ecomfe/react-hooks/tree/master/packages/intended-lazy",
9 | "bugs": {
10 | "url": "https://github.com/ecomfe/react-hooks/issues"
11 | },
12 | "license": "MIT",
13 | "main": "cjs/index.js",
14 | "module": "es/index.js",
15 | "types": "es/index.d.ts",
16 | "files": [
17 | "cjs",
18 | "es",
19 | "src"
20 | ],
21 | "scripts": {
22 | "build": "rm -rf es cjs && tsc & tsc --module ESNext --outDir ./es",
23 | "build-check": "tsc",
24 | "lint": "skr lint --strict src demo",
25 | "test": "skr test --coverage"
26 | },
27 | "devDependencies": {
28 | "@reskript/cli": "^1.10.1",
29 | "@reskript/cli-lint": "^1.10.1",
30 | "@reskript/cli-test": "^1.10.1",
31 | "@reskript/config-lint": "^1.10.1",
32 | "@testing-library/react-hooks": "^7.0.1",
33 | "@types/react": "^17.0.14",
34 | "antd": "^4.16.8",
35 | "react": "^17.0.0",
36 | "react-test-renderer": "^17.0.0",
37 | "typescript": "^4.3.5"
38 | },
39 | "peerDependencies": {
40 | "react": ">=16.8.0"
41 | },
42 | "publishConfig": {
43 | "access": "public",
44 | "registry": "https://registry.npmjs.com"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/packages/merged-ref/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@huse/merged-ref",
3 | "version": "1.2.1",
4 | "keywords": [
5 | "react",
6 | "hooks"
7 | ],
8 | "homepage": "https://github.com/ecomfe/react-hooks/tree/master/packages/merged-ref",
9 | "bugs": {
10 | "url": "https://github.com/ecomfe/react-hooks/issues"
11 | },
12 | "license": "MIT",
13 | "main": "cjs/index.js",
14 | "module": "es/index.js",
15 | "types": "es/index.d.ts",
16 | "files": [
17 | "cjs",
18 | "es",
19 | "src"
20 | ],
21 | "scripts": {
22 | "build": "rm -rf es cjs && tsc & tsc --module ESNext --outDir ./es",
23 | "build-check": "tsc",
24 | "lint": "skr lint --strict src demo",
25 | "test": "skr test --coverage"
26 | },
27 | "dependencies": {
28 | "@huse/previous-value": "^1.1.1"
29 | },
30 | "devDependencies": {
31 | "@reskript/cli": "^1.10.1",
32 | "@reskript/cli-lint": "^1.10.1",
33 | "@reskript/cli-test": "^1.10.1",
34 | "@reskript/config-lint": "^1.10.1",
35 | "@testing-library/react-hooks": "^7.0.1",
36 | "@types/react": "^17.0.14",
37 | "react": "^17.0.0",
38 | "typescript": "^4.3.5"
39 | },
40 | "peerDependencies": {
41 | "react": ">=16.8.0"
42 | },
43 | "publishConfig": {
44 | "access": "public",
45 | "registry": "https://registry.npmjs.com"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/packages/document-event/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@huse/document-event",
3 | "version": "1.1.1",
4 | "keywords": [
5 | "react",
6 | "hooks"
7 | ],
8 | "homepage": "https://github.com/ecomfe/react-hooks/tree/master/packages/document-event",
9 | "bugs": {
10 | "url": "https://github.com/ecomfe/react-hooks/issues"
11 | },
12 | "license": "MIT",
13 | "main": "cjs/index.js",
14 | "module": "es/index.js",
15 | "types": "es/index.d.ts",
16 | "files": [
17 | "cjs",
18 | "es",
19 | "src"
20 | ],
21 | "scripts": {
22 | "build": "rm -rf es cjs && tsc & tsc --module ESNext --outDir ./es",
23 | "build-check": "tsc",
24 | "lint": "skr lint --strict src demo",
25 | "test": "skr test --coverage --target=react"
26 | },
27 | "devDependencies": {
28 | "@reskript/cli": "^1.10.1",
29 | "@reskript/cli-lint": "^1.10.1",
30 | "@reskript/cli-test": "^1.10.1",
31 | "@reskript/config-lint": "^1.10.1",
32 | "@testing-library/react": "^12.0.0",
33 | "@types/react": "^17.0.14",
34 | "enzyme": "^3.11.0",
35 | "react": "^17.0.0",
36 | "react-dom": "^17.0.0",
37 | "typescript": "^4.3.5"
38 | },
39 | "peerDependencies": {
40 | "react": ">=16.8.0"
41 | },
42 | "publishConfig": {
43 | "access": "public",
44 | "registry": "https://registry.npmjs.com"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/packages/immer/docs/demo/useImmerReducer.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Button} from 'antd';
3 | import 'antd/dist/antd.min.css';
4 | import {useImmerReducer} from '@huse/immer';
5 |
6 | export default () => {
7 | const [state, dispatch] = useImmerReducer(
8 | (state, action) => {
9 | switch (action.type) {
10 | case 'inc':
11 | state.value++;
12 | break;
13 | case 'dec':
14 | state.value--;
15 | break;
16 | case 'reset':
17 | return {value: 0};
18 | default:
19 | return state;
20 | }
21 | },
22 | {value: 0}
23 | );
24 | return (
25 | <>
26 | current value: {state.value}
27 |
28 |
29 |
30 |
31 |
32 | >
33 | );
34 | };
--------------------------------------------------------------------------------
/packages/methods/docs/useMethodsExtension.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useMethodsExtension
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Methods
8 | path: /methods
9 | order: 3
10 | ---
11 |
12 | # useMethodsExtension
13 |
14 | Once you have a `setState` function from `useImmer` in [`use-immer`](https://github.com/immerjs/use-immer/), you can also wrap it to a methods object.
15 |
16 | ```typescript
17 | export function useMethodsExtension>(reducers: R, setState: SetImmerState): Methods
18 | ```
19 |
20 | This hook is also useful to extends more methods from an already generated methods hook.
21 |
22 | ```javascript
23 | const App = () => {
24 | // Suppose useArray is a hook implemented on useMethods
25 | const [list, methods, setList] = useArray();
26 | const extendedMethods = useMethodsExtension(
27 | {
28 | filterEnabled(state) {
29 | return state.filter(u => u.enabled);
30 | },
31 | },
32 | setList
33 | );
34 |
35 | // Now filterEnabled becomes a method to update list
36 | return (
37 | <>
38 | {/* other content */}
39 |
42 | >
43 | );
44 | };
45 | ```
46 |
--------------------------------------------------------------------------------
/packages/scroll-into-view/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@huse/scroll-into-view",
3 | "version": "1.1.1",
4 | "keywords": [
5 | "react",
6 | "hooks"
7 | ],
8 | "homepage": "https://github.com/ecomfe/react-hooks/tree/master/packages/scroll-into-view",
9 | "bugs": {
10 | "url": "https://github.com/ecomfe/react-hooks/issues"
11 | },
12 | "license": "MIT",
13 | "main": "cjs/index.js",
14 | "module": "es/index.js",
15 | "types": "es/index.d.ts",
16 | "files": [
17 | "cjs",
18 | "es",
19 | "src"
20 | ],
21 | "scripts": {
22 | "build": "rm -rf es cjs && tsc & tsc --module ESNext --outDir ./es",
23 | "build-check": "tsc",
24 | "lint": "skr lint --strict src demo",
25 | "start": "skr dev --src=demo",
26 | "test": "echo 'no test in @huse/scroll-into-view'"
27 | },
28 | "devDependencies": {
29 | "@reskript/cli": "^1.10.1",
30 | "@reskript/cli-dev": "^1.10.1",
31 | "@reskript/cli-lint": "^1.10.1",
32 | "@reskript/config-lint": "^1.10.1",
33 | "@types/react": "^17.0.14",
34 | "classnames": "^2.2.6",
35 | "react": "^17.0.0",
36 | "typescript": "^4.3.5",
37 | "webpack": "^5.45.1"
38 | },
39 | "peerDependencies": {
40 | "react": ">=16.8.0"
41 | },
42 | "publishConfig": {
43 | "access": "public",
44 | "registry": "https://registry.npmjs.com"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/packages/methods/src/native.ts:
--------------------------------------------------------------------------------
1 | import {useRef, Dispatch, SetStateAction, useState} from 'react';
2 | import {NativeReducers, Methods} from './interface';
3 |
4 | export type NativeMethodsHook> = [S, Methods, Dispatch>];
5 |
6 | export function useMethodsExtensionNative>(
7 | reducers: R,
8 | setState: Dispatch>
9 | ): Methods {
10 | const methodsRef = useRef | undefined>(undefined);
11 |
12 | if (!methodsRef.current) {
13 | methodsRef.current = Object.keys(reducers).reduce(
14 | (methods, key) => {
15 | const fn = reducers[key];
16 | const bound = (...args: any[]) => setState(s => fn(s, ...args));
17 | Object.assign(methods, {[key]: bound});
18 | return methods;
19 | },
20 | {} as Methods
21 | );
22 | }
23 |
24 | return methodsRef.current;
25 | }
26 |
27 | export function useMethodsNative>(
28 | reducers: R,
29 | initialState: S | (() => S)
30 | ): NativeMethodsHook {
31 | const [state, setState] = useState(initialState);
32 | const boundMethods = useMethodsExtensionNative(reducers, setState);
33 | return [state, boundMethods, setState];
34 | }
35 |
--------------------------------------------------------------------------------
/packages/snapshot/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@huse/snapshot",
3 | "version": "1.1.1",
4 | "keywords": [
5 | "react",
6 | "hooks"
7 | ],
8 | "homepage": "https://github.com/ecomfe/react-hooks/tree/master/packages/snapshot",
9 | "bugs": {
10 | "url": "https://github.com/ecomfe/react-hooks/issues"
11 | },
12 | "license": "MIT",
13 | "main": "cjs/index.js",
14 | "module": "es/index.js",
15 | "types": "es/index.d.ts",
16 | "files": [
17 | "cjs",
18 | "es",
19 | "src"
20 | ],
21 | "scripts": {
22 | "build": "rm -rf es cjs && tsc & tsc --module ESNext --outDir ./es",
23 | "build-check": "tsc",
24 | "lint": "skr lint --strict src demo",
25 | "test": "skr test --coverage"
26 | },
27 | "dependencies": {
28 | "@huse/debounce": "^1.1.1"
29 | },
30 | "devDependencies": {
31 | "@reskript/cli": "^1.10.1",
32 | "@reskript/cli-lint": "^1.10.1",
33 | "@reskript/cli-test": "^1.10.1",
34 | "@reskript/config-lint": "^1.10.1",
35 | "@testing-library/react-hooks": "^7.0.1",
36 | "@types/react": "^17.0.14",
37 | "antd": "^4.16.8",
38 | "react": "^17.0.0",
39 | "typescript": "^4.3.5"
40 | },
41 | "peerDependencies": {
42 | "react": ">=16.8.0"
43 | },
44 | "publishConfig": {
45 | "access": "public",
46 | "registry": "https://registry.npmjs.com"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/packages/update/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@huse/update",
3 | "version": "1.1.1",
4 | "keywords": [
5 | "react",
6 | "hooks"
7 | ],
8 | "homepage": "https://github.com/ecomfe/react-hooks/tree/master/packages/update",
9 | "bugs": {
10 | "url": "https://github.com/ecomfe/react-hooks/issues"
11 | },
12 | "license": "MIT",
13 | "main": "cjs/index.js",
14 | "module": "es/index.js",
15 | "types": "es/index.d.ts",
16 | "files": [
17 | "cjs",
18 | "es",
19 | "src"
20 | ],
21 | "scripts": {
22 | "build": "rm -rf es cjs && tsc & tsc --module ESNext --outDir ./es",
23 | "build-check": "tsc",
24 | "lint": "skr lint --strict src demo",
25 | "test": "skr test --coverage"
26 | },
27 | "devDependencies": {
28 | "@reskript/cli": "^1.10.1",
29 | "@reskript/cli-lint": "^1.10.1",
30 | "@reskript/cli-test": "^1.10.1",
31 | "@reskript/config-lint": "^1.10.1",
32 | "@testing-library/react-hooks": "^7.0.1",
33 | "@types/react": "^17.0.14",
34 | "antd": "^4.16.8",
35 | "react": "^17.0.0",
36 | "react-dom": "^17.0.0",
37 | "react-test-renderer": "^17.0.0",
38 | "typescript": "^4.3.5"
39 | },
40 | "peerDependencies": {
41 | "react": ">=16.8.0"
42 | },
43 | "publishConfig": {
44 | "access": "public",
45 | "registry": "https://registry.npmjs.com"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/packages/effect-ref/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@huse/effect-ref",
3 | "version": "1.1.1",
4 | "keywords": [
5 | "react",
6 | "hooks"
7 | ],
8 | "homepage": "https://github.com/ecomfe/react-hooks/tree/master/packages/effect-ref",
9 | "bugs": {
10 | "url": "https://github.com/ecomfe/react-hooks/issues"
11 | },
12 | "license": "MIT",
13 | "main": "cjs/index.js",
14 | "module": "es/index.js",
15 | "types": "es/index.d.ts",
16 | "files": [
17 | "cjs",
18 | "es",
19 | "src"
20 | ],
21 | "scripts": {
22 | "build": "rm -rf es cjs && tsc & tsc --module ESNext --outDir ./es",
23 | "build-check": "tsc",
24 | "lint": "skr lint --strict src demo",
25 | "test": "skr test --coverage --target=react"
26 | },
27 | "devDependencies": {
28 | "@reskript/cli": "^1.10.1",
29 | "@reskript/cli-lint": "^1.10.1",
30 | "@reskript/cli-test": "^1.10.1",
31 | "@reskript/config-lint": "^1.10.1",
32 | "@testing-library/react": "^12.0.0",
33 | "@types/react": "^17.0.14",
34 | "antd": "^4.16.8",
35 | "enzyme": "^3.11.0",
36 | "react": "^17.0.0",
37 | "react-dom": "^17.0.0",
38 | "typescript": "^4.3.5"
39 | },
40 | "peerDependencies": {
41 | "react": ">=16.8.0"
42 | },
43 | "publishConfig": {
44 | "access": "public",
45 | "registry": "https://registry.npmjs.com"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/packages/scroll-position/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@huse/scroll-position",
3 | "version": "1.1.1",
4 | "keywords": [
5 | "react",
6 | "hooks"
7 | ],
8 | "homepage": "https://github.com/ecomfe/react-hooks/tree/master/packages/scroll-position",
9 | "bugs": {
10 | "url": "https://github.com/ecomfe/react-hooks/issues"
11 | },
12 | "license": "MIT",
13 | "main": "cjs/index.js",
14 | "module": "es/index.js",
15 | "types": "es/index.d.ts",
16 | "files": [
17 | "cjs",
18 | "es",
19 | "src"
20 | ],
21 | "scripts": {
22 | "build": "rm -rf es cjs && tsc & tsc --module ESNext --outDir ./es",
23 | "build-check": "tsc",
24 | "lint": "skr lint --strict src demo",
25 | "test": "skr test --coverage --target=react"
26 | },
27 | "dependencies": {
28 | "has-passive-events": "^1.0.0"
29 | },
30 | "devDependencies": {
31 | "@reskript/cli": "^1.10.1",
32 | "@reskript/cli-lint": "^1.10.1",
33 | "@reskript/cli-test": "^1.10.1",
34 | "@reskript/config-lint": "^1.10.1",
35 | "@testing-library/react-hooks": "^7.0.1",
36 | "@types/react": "^17.0.14",
37 | "react": "^17.0.0",
38 | "typescript": "^4.3.5"
39 | },
40 | "peerDependencies": {
41 | "react": ">=16.8.0"
42 | },
43 | "publishConfig": {
44 | "access": "public",
45 | "registry": "https://registry.npmjs.com"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/packages/selection/docs/useSelection.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: useSelection
3 | nav:
4 | title: Hooks
5 | path: /hook
6 | group:
7 | title: Selection
8 | path: /selection
9 | order: 2
10 | ---
11 |
12 | # useSelection
13 |
14 | This hook provides fundamental capabilities to select single, multiple or a range of items in a list.
15 |
16 | ```typescript
17 | interface SelectionOptions {
18 | multiple?: boolean; // allow multiple selection using CMD or CTRL
19 | range?: boolean; // allow range selection using SHIFT
20 | }
21 |
22 | interface ClickContext {
23 | ctrlKey: boolean;
24 | metaKey: boolean;
25 | shiftKey: boolean;
26 | }
27 |
28 | interface SelectionMethods {
29 | selectIndex(index: number, e?: ClickContext): void;
30 | }
31 |
32 | type SelectionHook = [number[], SelectionMethods];
33 |
34 | function useSelection(initialSelection: number[] = [], options?: SelectionOptions): SelectionHook;
35 | ```
36 |
37 | `useSelection` is an index based interface where the content of list is not important.
38 | The returned number array contains a set of selected zero-based item index.
39 |
40 | The `selectIndex` function in `SelectionMethods` accepts an zero-based index and a `ClickContext`,
41 | usually `MouseEvent` instance are compatible with `ClickContext` so you can directly pass an event object to it.
42 |
43 |
44 |
--------------------------------------------------------------------------------
/docs/index.en-US.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "huse - React Hooks Library"
3 | hero:
4 | title: huse
5 | desc: Hook To Use.
6 | actions:
7 | - text: Getting Started
8 | link: /docs/getting-started
9 | features:
10 | - icon: https://gw.alipayobjects.com/zos/bmw-prod/813f5ed9-6bc4-43d4-9f74-ec81ecf35733/k7htg6n4_w144_h144.png
11 | title: Rich Collection
12 | desc: huse core contains a large set of essential react hooks, with demos and examples for each one of them.
13 | - icon: https://gw.alipayobjects.com/zos/bmw-prod/7659205c-6637-4fa2-8529-d32e5818304b/k7htflfb_w144_h144.png
14 | title: Detailed Tutorial
15 | desc: Providing tutorials about using hooks in practice. An all-in-one place to learn for pro coders and newcomers.
16 | - icon: https://gw.alipayobjects.com/zos/bmw-prod/6319a122-e8b8-497f-9b45-37cfbe77edaa/k7htfx7t_w144_h144.png
17 | title: Ready for Production
18 | desc: Used in hundreds of products in Baidu, with high quality and robustness.
19 | footer: Open-source MIT Licensed | Copyright © 2020
Powered by [Baidu EFE team](https://ecomfe.github.io/)
20 | ---
21 |
22 | ## Easy to get started
23 |
24 | Install dependency:
25 |
26 | ```bash
27 | npm install huse --save
28 | ```
29 |
30 | Or with `yarn`:
31 |
32 | ```bash
33 | yarn add huse
34 | ```
35 |
36 | Use Hooks
37 |
38 | ```bash
39 | import { useBoolean } from 'huse';
40 | ```
41 |
--------------------------------------------------------------------------------
/packages/document-title/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@huse/document-title",
3 | "version": "1.1.1",
4 | "keywords": [
5 | "react",
6 | "hooks"
7 | ],
8 | "homepage": "https://github.com/ecomfe/react-hooks/tree/master/packages/document-title",
9 | "bugs": {
10 | "url": "https://github.com/ecomfe/react-hooks/issues"
11 | },
12 | "license": "MIT",
13 | "main": "cjs/index.js",
14 | "module": "es/index.js",
15 | "types": "es/index.d.ts",
16 | "files": [
17 | "cjs",
18 | "es",
19 | "src"
20 | ],
21 | "scripts": {
22 | "build": "rm -rf es cjs && tsc & tsc --module ESNext --outDir ./es",
23 | "build-check": "tsc",
24 | "lint": "skr lint --strict src demo",
25 | "test": "skr test --coverage --target=react"
26 | },
27 | "devDependencies": {
28 | "@reskript/cli": "^1.10.1",
29 | "@reskript/cli-lint": "^1.10.1",
30 | "@reskript/cli-test": "^1.10.1",
31 | "@reskript/config-lint": "^1.10.1",
32 | "@testing-library/react": "^12.0.0",
33 | "@types/react": "^17.0.14",
34 | "antd": "^4.16.8",
35 | "enzyme": "^3.11.0",
36 | "react": "^17.0.0",
37 | "react-dom": "^17.0.0",
38 | "typescript": "^4.3.5"
39 | },
40 | "peerDependencies": {
41 | "react": ">=16.8.0"
42 | },
43 | "publishConfig": {
44 | "access": "public",
45 | "registry": "https://registry.npmjs.com"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------