TinyBase logoTinyBase β

Building A UI With Indexes

This guide covers how the ui-react module supports the Indexes object.

As with the React-based bindings to a Store object, the ui-react module provides both hooks and components to connect your indexes to your interface.

Indexes Hooks

The useSliceIds hook is as simple as it sounds. It gets the current set of Slice Ids in an Index, and registers a listener so that any changes to that result will cause a re-render:

import {createIndexes, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useSliceIds} from 'tinybase/ui-react';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});
const indexes = createIndexes(store);
indexes.setIndexDefinition(
  'bySpecies', // indexId
  'pets', //      tableId to index
  'species', //   cellId to index on
);
const App = () => (
  <span>{JSON.stringify(useSliceIds('bySpecies', indexes))}</span>
);

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
console.log(app.innerHTML);
// -> '<span>["dog","cat"]</span>'

store.setRow('pets', 'lowly', {species: 'worm'});
console.log(app.innerHTML);
// -> '<span>["dog","cat","worm"]</span>'

The useCreateIndexes hook is used to create an Indexes object within a React application with convenient memoization:

import {useCreateIndexes, useCreateStore} from 'tinybase/ui-react';

const App2 = () => {
  const store = useCreateStore(() =>
    createStore().setTable('pets', {
      fido: {species: 'dog'},
      felix: {species: 'cat'},
      cujo: {species: 'dog'},
    }),
  );
  const indexes = useCreateIndexes(store, (store) =>
    createIndexes(store).setIndexDefinition('bySpecies', 'pets', 'species'),
  );
  return <span>{JSON.stringify(useSliceIds('bySpecies', indexes))}</span>;
};

root.render(<App2 />);
console.log(app.innerHTML);
// -> '<span>["dog","cat"]</span>'

Index And Slice Views

The IndexView component renders the structure of an Index, and registers a listener so that any changes to that result will cause a re-render. The SliceView component renders just a single Slice by iterating over each Row in that Slice.

As with all ui-react view components, these use their corresponding hooks under the covers, which means that any changes to the Index or the Row objects referenced by it will cause a re-render.

import {SliceView} from 'tinybase/ui-react';

const App3 = () => (
  <div>
    <SliceView
      indexId="bySpecies"
      sliceId="dog"
      indexes={indexes}
      debugIds={true}
    />
  </div>
);

root.render(<App3 />);
console.log(app.innerHTML);
// -> '<div>dog:{fido:{species:{dog}}cujo:{species:{dog}}}</div>'

A SliceView can be given a custom RowView-compatible component to render its children, much like a TableView component can. And an IndexView can be in turn given a custom SliceView-compatible component:

import {IndexView} from 'tinybase/ui-react';

const MyRowView = (props) => <>{props.rowId};</>;

const MySliceView = (props) => (
  <div>
    {props.sliceId}:<SliceView {...props} rowComponent={MyRowView} />
  </div>
);

const App4 = () => (
  <IndexView
    indexId="bySpecies"
    indexes={indexes}
    sliceComponent={MySliceView}
  />
);

root.render(<App4 />);
console.log(app.innerHTML);
// -> '<div>dog:fido;cujo;</div><div>cat:felix;</div><div>worm:lowly;</div>'

Indexes Context

In the same way that a Store can be passed into a Provider component context and used throughout the app, an Indexes object can also be provided to be used by default:

import {Provider, useSliceRowIds} from 'tinybase/ui-react';

const App5 = () => {
  const store = useCreateStore(() =>
    createStore().setTable('pets', {
      fido: {species: 'dog'},
      felix: {species: 'cat'},
      cujo: {species: 'dog'},
    }),
  );
  const indexes = useCreateIndexes(store, (store) =>
    createIndexes(store).setIndexDefinition('bySpecies', 'pets', 'species'),
  );

  return (
    <Provider indexes={indexes}>
      <Pane />
    </Provider>
  );
};

const Pane = () => (
  <span>
    <SliceView indexId="bySpecies" sliceId="dog" debugIds={true} />/
    {useSliceRowIds('bySpecies', 'cat')}
  </span>
);

root.render(<App5 />);
console.log(app.innerHTML);
// -> '<span>dog:{fido:{species:{dog}}cujo:{species:{dog}}}/felix</span>'

The indexesById prop can be used in the same way that the storesById prop is, to let you reference multiple Indexes objects by Id.

Summary

The support for Indexes objects in the ui-react module is very similar to that for the Store object and Metrics object, making it easy to attach Index and Slice contents to your user interface.

We finish off this section about the indexes module with the Advanced Index Definition guide.