diff --git a/README.md b/README.md index cc1728fafd..8c27a9bb07 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,7 @@ - [`useCookie`](./docs/useCookie.md) — provides way to read, update and delete a cookie. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/side-effects-usecookie--demo) - [`useCopyToClipboard`](./docs/useCopyToClipboard.md) — copies text to clipboard. - [`useDebounce`](./docs/useDebounce.md) — debounces a function. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/side-effects-usedebounce--demo) + - [`useDocumentTitle`](./docs/useDocumentTitle.md) — changes document title. - [`useError`](./docs/useError.md) — error dispatcher. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/side-effects-useerror--demo) - [`useFavicon`](./docs/useFavicon.md) — sets favicon of the page. - [`useLocalStorage`](./docs/useLocalStorage.md) — manages a value in `localStorage`. diff --git a/docs/useDocumentTitle.md b/docs/useDocumentTitle.md new file mode 100644 index 0000000000..d5c10fa108 --- /dev/null +++ b/docs/useDocumentTitle.md @@ -0,0 +1,31 @@ +# `useDocumentTitle` + +React hook that updates the `document.title` when the component mounts or when the title changes. +It also restores the previous title on unmount. + +## Usage + +```jsx +import { useDocumentTitle } from 'react-use'; + +const Demo = () => { + const [count, setCount] = React.useState(0); + + useDocumentTitle(`Clicked ${count} times`); + + return ( +
+

Click the button to update document title

+ +
+ ); +}; +``` + +## Reference + +```ts +useDocumentTitle(title); +``` diff --git a/src/index.ts b/src/index.ts index 62b69356b7..04407fbfbc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,6 +18,7 @@ export { default as useCustomCompareEffect } from './useCustomCompareEffect'; export { default as useDebounce } from './useDebounce'; export { default as useDeepCompareEffect } from './useDeepCompareEffect'; export { default as useDefault } from './useDefault'; +export { default as useDocumentTitle } from './useDocumentTitle'; export { default as useDrop } from './useDrop'; export { default as useDropArea } from './useDropArea'; export { default as useEffectOnce } from './useEffectOnce'; diff --git a/src/useDocumentTitle.ts b/src/useDocumentTitle.ts new file mode 100644 index 0000000000..62ce639b8f --- /dev/null +++ b/src/useDocumentTitle.ts @@ -0,0 +1,12 @@ +import { useEffect } from 'react'; + +export default function useDocumentTitle(title: string) { + useEffect(() => { + const previousTitle = document.title; + document.title = title; + + return () => { + document.title = previousTitle; + }; + }, [title]); +} diff --git a/stories/useDocumentTitle.story.tsx b/stories/useDocumentTitle.story.tsx new file mode 100644 index 0000000000..1a01527ca2 --- /dev/null +++ b/stories/useDocumentTitle.story.tsx @@ -0,0 +1,22 @@ +import { storiesOf } from '@storybook/react'; +import * as React from 'react'; +import { useDocumentTitle } from '../src'; +import ShowDocs from './util/ShowDocs'; + +const Demo = () => { + const [count, setCount] = React.useState(0); + + // Update document title whenever count changes + useDocumentTitle(`Clicked ${count} times`); + + return ( +
+

Click the button to update document title

+ +
+ ); +}; + +storiesOf('SideEffects/useDocumentTitle', module) + .add('Docs', () => ) + .add('Demo', () => ); diff --git a/tests/useDocumentTitle.test.ts b/tests/useDocumentTitle.test.ts new file mode 100644 index 0000000000..f487978b2d --- /dev/null +++ b/tests/useDocumentTitle.test.ts @@ -0,0 +1,22 @@ +import { renderHook } from '@testing-library/react-hooks'; +import { useDocumentTitle } from '../src'; + +it('should set the document title on mount', () => { + const title = 'Doc Title'; + + renderHook(() => useDocumentTitle(title)); + + expect(document.title).toBe(title); +}); + +it('should update the title when argument changes', () => { + const { rerender } = renderHook(({ title }) => useDocumentTitle(title), { + initialProps: { title: 'First Title' }, + }); + + expect(document.title).toBe('First Title'); + + rerender({ title: 'Second Title' }); + + expect(document.title).toBe('Second Title'); +});