1.0.0 • Published 2 years ago

@browser-search/react-browser-search v1.0.0

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

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

Yarn

yarn add @browser-search/browser-search
yarn add @browser-search/react-browser-search

Npm

npm install -S @browser-search/browser-search
npm install -S @browser-search/react-browser-search

Usage 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

see createStore

Signature

<TDocument>(): [(request: CreateStoreRequest<TDocument>) =>  Promise<void>, QueryState<TDocument>]

Generics

  • TDocument is 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: string the name of the store to create - indexConfig: SimplifiedIndexConfig<DataSchema> the fields to index. see reference here - keyPath: keyof TDocument is the field which is be the primary key. That field does not need to be included in the indexConfig above
  • queryState: 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

see addDocumentsToStore

Signature

<TDocument>(): [(request: AddDocumentsToStoreRequest<TDocument>) =>  Promise<void>, QueryState<TDocument>]

Generics

  • TDocument is 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: string the name of the store to create - documents: TDocument[] is the array of documents to be stored
  • queryState: 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

see queryStore

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

  • TDocument is the type of the document you will store
  • TFilterId is 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

see getIndexValues

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

  • T is the type of the property indexed, that should be compliant with the IDBValidKey interface, where IDBValidKey = number | string | Date | BufferSource | IDBValidKey[]

Parameters

  • request: GetIndexValuesRequest is the object containing the request parameters. - storeId: string: the store name - field: string is 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

see deleteStore

Signature

(): [(request: DeleteStoreRequest) =>  Promise<void>, QueryState]

Parameters

none

Return value

[(request: DeleteStoreRequest) =>  Promise<void>, QueryState]

With

  • request: DeleteStoreRequest is the object containing the request for the store deletion - storeId: string the name of the store to create
  • queryState: 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 | ErrorQueryState
  • IdleQueryState: 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 --> C

Definition

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>;