-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* Create useClientSideValue hook #116 * Add initialValue option * Fix eslint warning
- Loading branch information
1 parent
eb6a8af
commit 1addfc3
Showing
12 changed files
with
5,478 additions
and
7,622 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { Meta } from '@storybook/blocks'; | ||
|
||
<Meta title="hooks/useClientSideValue" /> | ||
|
||
# useClientSideValue | ||
|
||
Hook that returns the value returned by a callback function that is only called on client-side. | ||
|
||
## Reference | ||
|
||
```ts | ||
function useClientSideValue<T extends () => unknown>(callback: T): ReturnType<T> | null; | ||
``` | ||
|
||
## Usage | ||
|
||
Use this hook when an API is not available on the server or may return a different result on the | ||
server to avoid hydration mismatches. | ||
|
||
Common use cases: | ||
|
||
- Date APIs | ||
- Intl APIs | ||
|
||
```tsx | ||
type MyComponentProps = { | ||
serverTime: Date; | ||
}; | ||
|
||
function MyComponent({ serverTime }): ReactElement { | ||
const now = useClientSideValue(Date.now, serverTime); | ||
|
||
return <>{now ?? 'n/a'}</>; | ||
} | ||
``` | ||
|
||
```tsx | ||
type MyComponentProps = { | ||
bytes: number; | ||
}; | ||
|
||
function MyComponent({ bytes }: MyComponentProps): ReactElement { | ||
const numberFormat = useClientSideValue( | ||
() => | ||
new Intl.NumberFormat(process.env.LOCALE, { | ||
notation: 'compact', | ||
style: 'unit', | ||
unit: 'byte', | ||
unitDisplay: 'narrow', | ||
}), | ||
); | ||
|
||
return <>{numberFormat?.format(bytes) ?? bytes}</>; | ||
} | ||
``` |
50 changes: 50 additions & 0 deletions
50
src/hooks/useClientSideValue/useClientSideValue.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* eslint-disable react-hooks/rules-of-hooks */ | ||
/* eslint-disable react/jsx-no-literals */ | ||
import type { StoryObj } from '@storybook/react'; | ||
import { useClientSideValue } from './useClientSideValue.js'; | ||
|
||
export default { | ||
title: 'hooks/useClientSideValue', | ||
}; | ||
|
||
const note = ( | ||
<div className="alert alert-secondary"> | ||
<h4 className="alert-heading">Note!</h4> | ||
<p className="mb-0"> | ||
The initial value before the mount is different in a server-side rendered context, this is not | ||
visible in the documentation. | ||
</p> | ||
</div> | ||
); | ||
|
||
export const Demo: StoryObj = { | ||
render() { | ||
const value = useClientSideValue(Date.now, 0); | ||
|
||
return ( | ||
<> | ||
<div> | ||
Value: <span className="badge rounded-pill bg-primary">{value}</span> | ||
</div> | ||
|
||
{note} | ||
</> | ||
); | ||
}, | ||
}; | ||
|
||
export const Nullable: StoryObj = { | ||
render() { | ||
const value = useClientSideValue<number | null>(Date.now, null); | ||
|
||
return ( | ||
<> | ||
<div> | ||
Value: <span className="badge rounded-pill bg-primary">{value}</span> | ||
</div> | ||
|
||
{note} | ||
</> | ||
); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { renderHook } from '@testing-library/react'; | ||
import { useClientSideValue } from './useClientSideValue.js'; | ||
|
||
describe('useClientSideValue', () => { | ||
it('should not crash', async () => { | ||
renderHook(() => useClientSideValue(Date.now, 0)); | ||
}); | ||
|
||
it('should set the value once', async () => { | ||
let count = 0; | ||
|
||
const { result, rerender } = renderHook(() => useClientSideValue(() => ++count, 0)); | ||
|
||
expect(result.current).toBe(1); | ||
|
||
rerender(); | ||
|
||
expect(result.current).toBe(1); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { useState } from 'react'; | ||
import { useMount } from '../useMount/useMount.js'; | ||
|
||
/** | ||
* Hook that returns the value returned by a callback function that is only called on client-side. | ||
* | ||
* @example | ||
* function MyComponent() { | ||
* const value = useClientSideValue(Date.now, null); | ||
* | ||
* return <div>{value ?? 'n/a'}</div>; | ||
* } | ||
*/ | ||
export function useClientSideValue<T>(callback: () => T, initialValue?: T): typeof initialValue { | ||
const [value, setValue] = useState(initialValue); | ||
|
||
useMount(() => { | ||
setValue(callback); | ||
}); | ||
|
||
return value; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters