Skip to content
Open
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
30 changes: 12 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,20 @@ npm install --save react-use-flexsearch
## API

```js
useFlexSearch(query: String, index: Index! | String!, store: Object!, options: Object) => Object[]
useFlexSearch(query: String | Object, index: Index! | String!, options: Object) => Object[]
```

The `useFlexSearch` [hook][hooks] takes your search query, index, and store and
returns results as an array. Searches are memoized to ensure efficient
searching.
The `useFlexSearch` [hook][hooks] takes your search query, index, and search
options and returns results as an array. Searches are memoized to ensure
efficient searching.

### Parameters

| Name | Type | Description |
| ------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| **`query`** | `String` | The search query. As this value updates, the return value will be updated. |
| **`index`** | `Index \| String` | **Required**. The FlexSearch index. This can be an instance of a FlexSearch index or one that has been exported via `Index.export`. |
| **`store`** | `Object` | **Required**. Object mapping a result `id` to an object of data. |
| **`options`** | `Object` | Search options passed to `Index.search`. |
| Name | Type | Description |
| ------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| **`query`** | `String \| Object` | The search query. As this value updates, the return value will be updated. |
| **`index`** | `Index \| String` | **Required**. The FlexSearch index. This can be an instance of a FlexSearch index or one that has been exported via `Index.export`. |
| **`options`** | `Object` | Search options passed to `Index.search`. |

### Example

Expand All @@ -40,21 +39,16 @@ Note: [Formik][formik] is used in the following example to handle form state,
but is not required. As long as your query is passed as the first parameter,
you can manage how to store it.

```js
```jsx
import React, { useState } from 'react'
import { useFlexSearch } from 'react-use-flexsearch'
import { Formik, Form, Field } from 'formik'

const index = /* a FlexSearch index */
const store = {
1: { id: 1, title: 'Document 1' },
2: { id: 2, title: 'Document 2' },
3: { id: 3, title: 'Document 3' },
}

const SearchBar = () => {
const [query, setQuery] = useState(null)
const results = useFlexSearch(query, index, store)
const results = useFlexSearch(query, index)

return (
<Formik
Expand All @@ -70,7 +64,7 @@ const SearchBar = () => {
<h1>Results</h1>
<ul>
{results.map(result => (
<li key={result.id}>{result.title}</li>
<li key={result}>Result ID: {result}</li>
))}
</ul>
</Formik>
Expand Down
16 changes: 4 additions & 12 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,13 @@ import FlexSearch from 'flexsearch'
const InvalidIndexError = new Error(
'FlexSearch index is required. Check that your index exists and is valid.',
)
const InvalidStoreError = new Error(
'FlexSearch store is required. Check that your store exists and is valid.',
)

export const useFlexSearch = (query, providedIndex, store, searchOptions) => {
export const useFlexSearch = (query, providedIndex, options) => {
const [index, setIndex] = useState(null)

useEffect(() => {
if (!providedIndex) throw InvalidIndexError
if (!store) throw InvalidStoreError
}, [providedIndex, store])

useEffect(() => {
if (providedIndex instanceof FlexSearch) {
setIndex(providedIndex)

Expand All @@ -30,10 +24,8 @@ export const useFlexSearch = (query, providedIndex, store, searchOptions) => {
}, [providedIndex])

return useMemo(() => {
if (!query || !index || !store) return []

const rawResults = index.search(query, searchOptions)
if (!query || !index) return []

return rawResults.map(id => store[id])
}, [query, index, store])
return index.search(query, options)
}, [query, index])
}
80 changes: 30 additions & 50 deletions src/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,6 @@ documents.forEach(doc => {

const exportedIndex = index.export()

const store = {
[documents[0].id]: documents[0],
[documents[1].id]: documents[1],
[documents[2].id]: documents[2],
}

beforeEach(() => {
console.error = jest.fn()
})
Expand All @@ -47,69 +41,55 @@ afterEach(() => {

describe('useFlexSearch', () => {
test('returns empty results if no query', () => {
let objResults
testHook(() => (objResults = useFlexSearch(null, index, store)))

let exportedResults
testHook(
() => (exportedResults = useFlexSearch(null, exportedIndex, store)),
)
let results
testHook(() => (results = useFlexSearch(null, index)))

expect(objResults).toEqual([])
expect(exportedResults).toEqual([])
expect(results).toEqual([])
})

test('returns empty results if query has no matches', () => {
let objResults
testHook(() => (objResults = useFlexSearch('nomatches', index, store)))
let results
testHook(() => (results = useFlexSearch('nomatches', index)))

expect(results).toEqual([])
})

test('returns results if query has matches', () => {
let results
testHook(() => (results = useFlexSearch(documents[0].name, index)))

expect(results).toEqual([0])
})

test('returns same results using instance and exported index', () => {
let instanceResults
testHook(() => (instanceResults = useFlexSearch(documents[0].name, index)))

let exportedResults
testHook(
() =>
(exportedResults = useFlexSearch('nomatches', exportedIndex, store)),
() => (exportedResults = useFlexSearch(documents[0].name, exportedIndex)),
)

expect(objResults).toEqual([])
expect(exportedResults).toEqual([])
expect(instanceResults).toEqual([0])
expect(instanceResults).toEqual(exportedResults)
})

test('returns results if query has matches', () => {
let objResults
testHook(
() => (objResults = useFlexSearch(documents[0].name, index, store)),
)
test('returns results if query has matches using search options', () => {
let results
testHook(() => (results = useFlexSearch('JavaScript', index)))

let exportedResults
let limitedResults
testHook(
() =>
(exportedResults = useFlexSearch(
documents[0].name,
exportedIndex,
store,
)),
() => (limitedResults = useFlexSearch('JavaScript', index, { limit: 1 })),
)

expect(objResults).toEqual([documents[0]])
expect(exportedResults).toEqual([documents[0]])
expect(results).toEqual([1, 2])
expect(limitedResults).toEqual([1])
})

test('throws if index is missing', () => {
expect(() => {
testHook(() => useFlexSearch(documents[0].name, null, store))
}).toThrow('index is required')

expect(() => {
testHook(() => useFlexSearch(documents[0].name, undefined, store))
testHook(() => useFlexSearch(documents[0].name))
}).toThrow('index is required')
})

test('throws if store is missing', () => {
expect(() => {
testHook(() => useFlexSearch(documents[0].name, index, null))
}).toThrow('store is required')

expect(() => {
testHook(() => useFlexSearch(documents[0].name, index, undefined))
}).toThrow('store is required')
})
})