@browser-search/react-browser-search v1.0.0
react-browser-search
-> Take a look at the demo
This library provides react hooks to the browser-search library Please read the documentation of browser-search before using
- Get Started - Installation - Usage flow
- API Methods - useCreateStore - Signature - Example - useAddDocumentsToStore - Signature - Example - useQuery - Signature - Example - useIndexValues - Signature - Example - useDeleteStore - Signature - Example - BrowserSearchProvider - Example
- API Interfaces - QueryState - Definition
Get Started
Installation
Yarn
yarn add @browser-search/browser-search
yarn add @browser-search/react-browser-searchNpm
npm install -S @browser-search/browser-search
npm install -S @browser-search/react-browser-searchUsage flow
graph LR
0[Wrap components with provider]--> A
A[Create document store]--> B[Add Documents]
B --> C[Run a query]
B --> E[etc.]Step 1 - Wrap your component tree in the react-browser-search provider
For the hooks to work, you need to wrap your component tree in the provider which contains a client + cache layer. See BrowserSearchProvider for usage
Step 2 - Create a store
To get started, create a store that will later hold your data. You need to know in advance
- the type of the documents you will store
- the fields that you will use for filtering / sorting. Those fields must be indexed. See useCreateStore
Step 3 - Add documents
Then you can add documents to the newly created store See useAddDocumentsToStore
Step 4 - run a search query
You can now run complex queries to filter and sort your document, and display them to your users. See useQuery for usage
API Methods
useCreateStore
Signature
<TDocument>(): [(request: CreateStoreRequest<TDocument>) => Promise<void>, QueryState<TDocument>]Generics
TDocumentis the type of the document you will store
Parameters
none
Return value
[(request: CreateStoreRequest<TDocument>) => Promise<void>, QueryState<TDocument>]With
request: CreateStoreRequest<TDocument>is the object containing the request for the store creation -storeId: stringthe name of the store to create -indexConfig: SimplifiedIndexConfig<DataSchema>the fields to index. see reference here -keyPath: keyof TDocumentis the field which is be the primary key. That field does not need to be included in theindexConfigabovequeryState: UseCreateStoreQueryState: see QueryState
Example
Let's say we want to store books of the following type:
export interface Book {
isbn: string; // primary key
title: string;
releaseDate: string;
authors: string[];
categories: Array<'fantasy' | 'sci-fi' | 'thriller'>;
description: string;
}We want to be able to filter and sort on every field but the description.
import { SimplifiedIndexConfig } from '@browser-search/browser-search';
import { useCreateStore, UseCreateStoreQueryState } from '@browser-search/react-browser-search';
const storeName = "bookLibrary";
const indexConfig: SimplifiedIndexConfig<Book> = {
simple: ['title', 'releaseDate'],
array: ['authors', 'categories'],
};
const keyPath = 'isbn';
const useCreateLibraryStore = (): [() => Promise<void>, UseCreateStoreQueryState<Book>] => {
const [createStore, createStoreQueryState] = useCreateStore<Book>();
const createLibraryStore = () => createStore({
storeId,
indexConfig,
keyPath,
});
return [createPersonStore, createStoreQueryState];
};useAddDocumentsToStore
Signature
<TDocument>(): [(request: AddDocumentsToStoreRequest<TDocument>) => Promise<void>, QueryState<TDocument>]Generics
TDocumentis the type of the document you will store
Parameters
none
Return value
[(request: AddDocumentsToStoreRequest<TDocument>) => Promise<void>, QueryState<TDocument>]With
request: AddDocumentsToStoreRequest<TDocument>is the object containing the request to add the data to the store -storeId: stringthe name of the store to create -documents: TDocument[]is the array of documents to be storedqueryState: UseAddDocumentsToStoreQueryState: see QueryState
Example
import { useAddDocumentsToStore, UseAddDocumentsToStoreQueryState } from '@browser-search/react-browser-search';
const storeId = 'bookLibrary';
const useAddBooksToLibraryStore = (): [(books: Book[]) => Promise<void>, UseAddDocumentsToStoreQueryState<Book>] => {
const [addDocumentsToStore, addDocumentsToStoreQueryState] = useAddDocumentsToStore<Person>();
const addBooksToLibraryStore = (books: Book[]) => {
return addDataToStore({
storeId,
documents: books
})
}
return [addBooksToLibraryStore, addDocumentsToStoreQueryState];
}useQuery
Hook behaviour:
- Every query response is cached
- The cache is automatically invalidated at any store mutation and the request is automatically re-triggered
- A request automatically cancels a previously pending request
Signature
<TDocument, TFilterId extends string = string>(request: QueryRequest<TDocument, TFilterId>): QueryState<TDocument, TFilterId>Generics
TDocumentis the type of the document you will storeTFilterIdis the string union of all the filters ids defined in the filterConfiguration object, passed in the request. Defaults to a string.
Parameters
request: QueryRequest<TDocument, TFilterId>is the object containing the query parameters. Refer to QueryRequest
Return value
UseQueryQueryState<TDocument, TFilterId>see QueryState
Example
import { useQuery, UseQueryQueryState } from '@browser-search/react-browser-search';
import { QueryRequest, FilterConfig } from '@browser-search/browser-search';
type FilterIds = 'categoryFantasy' | 'categorySciFi' | 'categoryThriller';
const storeId = 'bookLibrary';
export const useBookQuery = (filterApplied: FilterIds[]): UseQueryQueryState<Book, FilterId> => {
const filterConfig: FilterConfig<Book, FilterIds> =
[
[
{ id: 'categoryFantasy', field: 'categories', operator: 'contains', operand: 'fantasy' },
{ id: 'categorySciFi', field: 'categories', operator: 'contains', operand: 'sci-fi' },
{ id: 'categoryThriller', field: 'categories', operator: 'contains', operand: 'thriller' },
],
];
const queryRequest: QueryRequest<Person, FilterId> = () => (
storeId,
filterConfig,
filtersApplied, // the ids of the filter in the filter configuration that you are filtering on. Example: ['categoryFantasy', 'categorySciFi']
orderBy: 'releaseDate',
orderDirection: 'DESC',
perPage: 10,
page: 0,
};
return useQuery<Book, FilterId>(queryRequest);
}useIndexValues
Hook behaviour:
- Every query response is cached.
- The cache is automatically invalidated on any store mutation and the request is automatically re-triggered
Signature
<T extends IDBValidKey>({storeId, field}: GetIndexValuesRequest): QueryState<T>Generics
Tis the type of the property indexed, that should be compliant with theIDBValidKeyinterface, whereIDBValidKey = number | string | Date | BufferSource | IDBValidKey[]
Parameters
request: GetIndexValuesRequestis the object containing the request parameters. -storeId: string: the store name -field: stringis the property for which you want to get all the values. It should be indexed at the store creation. See SimplifiedIndexConfig reference
Return value
UseIndexValuesQueryState<T>see QueryState
Example
import { useIndexValues, UseIndexValuesQueryState } from '@browser-search/react-browser-search';
const storeId = 'bookLibrary';
// to get the list of all the titles stored
export const useBookTitleValues = (): UseIndexValuesQueryState<string> => {
return useIndexValues<string>({
storedId,
field: 'title',
});
}useDeleteStore
Signature
(): [(request: DeleteStoreRequest) => Promise<void>, QueryState]Parameters
none
Return value
[(request: DeleteStoreRequest) => Promise<void>, QueryState]With
request: DeleteStoreRequestis the object containing the request for the store deletion -storeId: stringthe name of the store to createqueryState: UseDeleteStoreQueryState: see QueryState
Example
import { useDeleteStore, UseDeleteStoreQueryState } from '@browser-search/react-browser-search';
const useDeleteLibraryStore = (): [() => Promise<void>, UseDeleteStoreQueryState] => {
const [deleteStore, deleteStoreQueryState] = useDeleteStore();
const deleteLibraryStore = () => deleteStore({
storeId: "bookLibrary",
});
return [deleteLibraryStore, deleteStoreQueryState];
};BrowserSearchProvider
This react component connects your tree to the browser-search context
- any component calling a browser-search hook should be a child of this one
Example
import { BrowserSearchProvider } from '@browser-search/react-browser-search';
<BrowserSearchProvider>
<App />
</BrowserSearchProvider>API Interfaces
QueryState
This interface is returned by every hook. It is a react-state (meaning your component will re-render each time the query state changes) that represents the progress of the request.
QueryState is a union type that can take the following shape:
IdleQueryState | LoadingQueryState | SuccessQueryState | StaleQueryState | ErrorQueryStateIdleQueryState: when the request is not started. Example: a store mutation not yet called.LoadingQueryState: when the request is ongoing. React-state equivalent of a pending promise.SuccessQueryState: when the request has succeeded. React-state equivalent of a resolved promise.StaleQueryState: when a new request is loading after a previous one succeeded. Only returned for non-mutation hooks, ie. only for useIndexValues and useQuery. This state is useful when you still need access to the stale data while the new request is loading.ErrorQueryState: when the request failed. React-state equivalent of a rejected promise.
graph LR
0[IdleQueryState]-->A
A[LoadingQueryState]--> B[SuccessQueryState]
A --> C[ErrorQueryState]
B --> D[StaleQueryState]
D --> B
D --> CDefinition
interface IdleQueryState {
status: 'idle';
isFetching: false;
}
interface LoadingQueryState<Request> {
status: 'loading';
request: Request;
isFetching: true;
}
interface SuccessQueryState<Request, Response> {
status: 'success';
request: Request;
response: Response;
isFetching: false;
}
interface StaleQueryState<Request, Response> {
status: 'stale';
request: Request;
response: Response;
newRequest: Request;
isFetching: true;
}
interface ErrorQueryState<Request, Error>
status: 'error';
request: Request;
error: Error;
isFetching: false;
}
type QueryState<Request, Response, Error> = IdleQueryState | LoadingQueryState<Request> | SuccessQueryState<Request, Response> | StaleQueryState<Request, Response> | ErrorQueryState<Request, Error>;