Skip to content
This repository has been archived by the owner on Jan 10, 2025. It is now read-only.

Commit

Permalink
Merge pull request #1354 from Shopify/debounced-value
Browse files Browse the repository at this point in the history
Adds useDebouncedValue hook
  • Loading branch information
arthurgouveia authored Apr 8, 2020
2 parents d10ca79 + c4bbd1b commit cf6cf98
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 1 deletion.
6 changes: 5 additions & 1 deletion packages/react-hooks/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

<!-- ## [Unreleased] -->
## [Unreleased]

### Added

- Added `useDebouncedValue` hook ([#1354](https://github.com/Shopify/quilt/pull/1354))

## [1.6.1] - 2020-04-07

Expand Down
21 changes: 21 additions & 0 deletions packages/react-hooks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,27 @@ $ yarn add @shopify/react-hooks

## Usage

### `useDebouncedValue()`

This hook provide a debounced value.

```tsx
function MyComponent() {
const [searchValue, setSearchValue] = useState('');
const debouncedSearch = useDebouncedValue(searchValue);
const {data, loading} = useQuery(SomeQuery, {
variables: {
query: debouncedSearch,
},
});

function handleSearchTextChange(value: string) {
setSearchValue(value);
}

return (<input onChange={handleSearchTextChange} />);
```
### `useOnValueChange()`
This hook will track a given value and invoke a callback when it has changed.
Expand Down
26 changes: 26 additions & 0 deletions packages/react-hooks/src/hooks/debounced.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {useEffect, useState} from 'react';

interface DebouncedOptions {
timeoutMs: number;
}

const DEFAULT_DELAY = 500;

export function useDebouncedValue<T>(
value: T,
{timeoutMs}: DebouncedOptions = {timeoutMs: DEFAULT_DELAY},
) {
const [state, setState] = useState<T>(value);

useEffect(() => {
const timeout = window.setTimeout(() => {
setState(value);
}, timeoutMs);

return () => {
window.clearTimeout(timeout);
};
}, [value, timeoutMs]);

return state;
}
1 change: 1 addition & 0 deletions packages/react-hooks/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export {useDebouncedValue} from './debounced';
export {useInterval} from './interval';
export {useLazyRef} from './lazy-ref';
export {useMountedRef} from './mounted-ref';
Expand Down
55 changes: 55 additions & 0 deletions packages/react-hooks/src/hooks/tests/debounced.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React, {useState} from 'react';
import {mount} from '@shopify/react-testing';
import {timer} from '@shopify/jest-dom-mocks';

import {useDebouncedValue} from '../debounced';

const ONE_SECOND = 1000;
const HALF_A_SECOND = ONE_SECOND / 2;

describe('useDebouncedValue', () => {
beforeEach(() => {
timer.mock();
});

afterEach(() => {
timer.restore();
});

it('returns the first value when called once', () => {
const wrapper = setup({delay: ONE_SECOND, value: 'something'});

expect(wrapper).toContainReactText('something');
});

it('returns the debounced value', () => {
const initialValue = 'something';
const newValue = 'change';
const wrapper = setup({delay: ONE_SECOND, value: initialValue});

wrapper.setProps({value: newValue});

wrapper.act(() => {
timer.runTimersToTime(HALF_A_SECOND);
});

expect(wrapper).toContainReactText(initialValue);

wrapper.act(() => {
timer.runTimersToTime(ONE_SECOND);
});

expect(wrapper).toContainReactText(newValue);
});
});

function setup({delay, value}) {
const wrapper = mount(<MockComponent delay={delay} value={value} />);

return wrapper;
}

function MockComponent({value, delay}) {
const debouncedValue = useDebouncedValue(value, {timeoutMs: delay});
return <div>{debouncedValue}</div>;
}

0 comments on commit cf6cf98

Please sign in to comment.