Skip to content

Commit

Permalink
feat: add useHash hook
Browse files Browse the repository at this point in the history
  • Loading branch information
whinc authored and xobotyi committed Apr 8, 2020
1 parent 437719f commit 44a6cde
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 0 deletions.
39 changes: 39 additions & 0 deletions docs/useHash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# `useHash`

React sensor hook that tracks browser's location hash.

## Usage

```jsx
import {useHash} from 'react-use';

const Demo = () => {
const [hash, setHash] = useHash();

useMount(() => {
setHash('#/path/to/page?userId=123');
});

return (
<div>
<div>window.location.href:</div>
<div>
<pre>{window.location.href}</pre>
</div>
<div>Edit hash: </div>
<div>
<input style={{ width: '100%' }} value={hash} onChange={e => setHash(e.target.value)} />
</div>
</div>
);
};
```

## API

`const [hash, setHash] = useHash()`

Get latest url hash with `hash` and set url hash with `setHash`.

- `hash: string`: get current url hash. listen to `hashchange` event.
- `setHash: (newHash: string) => void`: change url hash. Invoke this method will trigger `hashchange` event.
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,4 @@ export { useRendersCount } from './useRendersCount';
export { useFirstMountState } from './useFirstMountState';
export { default as useSet } from './useSet';
export { createGlobalState } from './createGlobalState';
export { useHash } from './useHash'
27 changes: 27 additions & 0 deletions src/useHash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useState, useCallback } from "react"
import useLifecycles from "./useLifecycles"

/**
* read and write url hash, response to url hash change
*/
export const useHash = () => {
const [hash, setHash] = useState(() => window.location.hash)

const onHashChange = useCallback(() => {
setHash(window.location.hash)
}, [])

useLifecycles(() => {
window.addEventListener('hashchange', onHashChange)
}, () => {
window.removeEventListener('hashchange', onHashChange)
})

const _setHash = useCallback((newHash: string) => {
if (newHash !== hash) {
window.location.hash = newHash
}
}, [hash])

return [hash, _setHash] as const
}
29 changes: 29 additions & 0 deletions stories/useHash.story.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { storiesOf } from '@storybook/react';
import * as React from 'react';
import { useHash, useMount } from '../src';
import ShowDocs from './util/ShowDocs';

const Demo = () => {
const [hash, setHash] = useHash();

useMount(() => {
setHash('#/path/to/page?userId=123');
});

return (
<div>
<div>window.location.href:</div>
<div>
<pre>{window.location.href}</pre>
</div>
<div>Edit hash: </div>
<div>
<input style={{ width: '100%' }} value={hash} onChange={e => setHash(e.target.value)} />
</div>
</div>
);
};

storiesOf('Sensors|useHash', module)
.add('Docs', () => <ShowDocs md={require('../docs/useHash.md')} />)
.add('Demo', () => <Demo />);
54 changes: 54 additions & 0 deletions tests/useHash.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { renderHook, act } from '@testing-library/react-hooks';
import { useHash } from '../src/useHash';

(global as any).window = Object.create(window);
let mockHash = '#';
const mockLocation = {};
Object.defineProperty(mockLocation, 'hash', {
get() {
return mockHash;
},
set(newHash) {
mockHash = newHash;
window.dispatchEvent(new HashChangeEvent('hashchange'));
},
});
Object.defineProperty(window, 'location', {
value: mockLocation,
});

beforeEach(() => {
window.location.hash = '#';
});

test('returns current url hash', () => {
window.location.hash = '#abc';

const { result } = renderHook(() => useHash());

const hash = result.current[0];
expect(hash).toBe('#abc');
});

test('returns latest url hash when change the hash with setHash', () => {
const { result } = renderHook(() => useHash());
const hash = result.current[0];
const setHash = result.current[1];
expect(hash).toBe('#');
act(() => {
setHash('#abc');
});
const hash2 = result.current[0];
expect(hash2).toBe('#abc');
});

it('returns latest url hash when change the hash with "hashchange" event', () => {
const {result} = renderHook(() => useHash());
const hash = result.current[0]
expect(hash).toBe('#');
act(() => {
window.location.hash = '#abc'
})
const hash2 = result.current[0]
expect(hash2).toBe('#abc');
});

0 comments on commit 44a6cde

Please sign in to comment.