Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/bump-agent-version.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@fingerprint/react': patch
---

Bump `@fingerprint/agent` dependency to `^4.1.6`
5 changes: 5 additions & 0 deletions .changeset/tags-option-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@fingerprint/react': minor
---

Support `tags` in `useVisitorData` and deprecate `tag`
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,16 +191,16 @@ export default App

## Linking and tagging information

The visitor ID provided by Fingerprint Identification is especially useful when combined with information you already know about your users, for example, account IDs, order IDs, etc. To learn more about various applications of the `linkedId` and `tag`, see [Linking and tagging information](https://docs.fingerprint.com/docs/tagging-information).
The visitor ID provided by Fingerprint Identification is especially useful when combined with information you already know about your users, for example, account IDs, order IDs, etc. To learn more about various applications of the `linkedId` and `tags`, see [Linking and tagging information](https://docs.fingerprint.com/docs/tagging-information).

Associate the visitor ID with your data using the `linkedId` or `tag` parameter of the options object passed into the `useVisitorData()` hook or the `getData` function:
Associate the visitor ID with your data using the `linkedId` or `tags` parameter of the options object passed into the `useVisitorData()` hook or the `getData` function:

```jsx
// ...
function App() {
const { isLoading, error, getData } = useVisitorData({
linkedId: 'user_1234',
tag: {
tags: {
userAction: 'login',
analyticsId: 'UA-5555-1111-1',
},
Expand All @@ -209,6 +209,9 @@ function App() {
// ...
```

> [!NOTE]
> The `tag` option is deprecated in favor of `tags`. `tag` still works, but if both are provided, `tags` takes precedence.

## Error handling

The `getData` function throws errors directly from the JS Agent without changing them. See [JS Agent error handling](https://docs.fingerprint.com/reference/js-agent-error-handling) for more details.
Expand Down
39 changes: 39 additions & 0 deletions __tests__/use-visitor-data.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,22 @@ describe('useVisitorData', () => {
expect(mockGet).toHaveBeenCalledTimes(3)
})

it('should not deduplicate requests with different tags', async () => {
mockGet.mockImplementation(async () => {
await wait(250)
return mockGetResult
})

const wrapper = createWrapper()
const { result } = renderHook(() => useVisitorData({ immediate: false }), { wrapper })

await Promise.all([result.current.getData({ tags: { step: 1 } }), result.current.getData({ tags: { step: 2 } })])

expect(mockGet).toHaveBeenCalledTimes(2)
expect(mockGet).toHaveBeenNthCalledWith(1, { tags: { step: 1 } })
expect(mockGet).toHaveBeenNthCalledWith(2, { tags: { step: 2 } })
})

it('should reset fetched data while a subsequent request is pending', async () => {
const secondResult = { ...mockGetResult, visitor_id: 'second-visitor' }
let resolveSecondRequest!: (value: GetResult) => void
Expand Down Expand Up @@ -325,6 +341,29 @@ describe('useVisitorData', () => {
expect(mockGet).toHaveBeenNthCalledWith(2, { tag: 2 })
})

it('should re-fetch data when tags change if "immediate" is set to true', async () => {
const wrapper = createWrapper()
const { rerender } = renderHook(
({ step }: { step: number }) => useVisitorData({ immediate: true, tags: { step } }),
{
wrapper,
initialProps: { step: 1 },
}
)

await waitFor(() => {
expect(mockGet).toHaveBeenCalledTimes(1)
})

rerender({ step: 2 })

await waitFor(() => {
expect(mockGet).toHaveBeenCalledTimes(2)
})
expect(mockGet).toHaveBeenNthCalledWith(1, { tags: { step: 1 } })
expect(mockGet).toHaveBeenNthCalledWith(2, { tags: { step: 2 } })
})

it('should set error state when immediate mount fetch fails', async () => {
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => undefined)
mockGet.mockRejectedValue('mount failed')
Expand Down
16 changes: 8 additions & 8 deletions examples/create-react-app/src/shared/pages/SignInPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import VisitorDataPresenter from '../components/VisitorDataPresenter'
function SignInPage() {
const [login, setLogin] = useState('')
const [password, setPassword] = useState('')
const [tag, setTag] = useState('')
const [tags, setTags] = useState('')

const { getData, data, isLoading, error } = useVisitorData({ tag, linkedId: login, immediate: false })
const { getData, data, isLoading, error } = useVisitorData({ tags, linkedId: login, immediate: false })

return (
<section className='body'>
Expand Down Expand Up @@ -59,17 +59,17 @@ function SignInPage() {
/>
</div>
<div className='form-control'>
<label htmlFor='tag'>Tag:</label>
<label htmlFor='tags'>Tags:</label>
<input
id='tag'
id='tags'
type='text'
name='tag'
value={tag}
name='tags'
value={tags}
onChange={(e) => {
setTag(e.currentTarget.value)
setTags(e.currentTarget.value)
}}
/>
<sub>will be used as tag param for the request</sub>
<sub>will be used as tags param for the request</sub>
</div>
<button type='submit'>Submit</button>
</form>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
},
"homepage": "https://github.com/fingerprintjs/react#readme",
"dependencies": {
"@fingerprint/agent": "^4.1.2"
"@fingerprint/agent": "^4.1.6"
},
"peerDependencies": {
"react": ">=18 <20"
Expand Down
Loading
Loading