Skip to content

Instantly share code, notes, and snippets.

@Haroenv
Created September 5, 2019 08:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Haroenv/d64495d439f3c28fcba8b983b8c9b2d7 to your computer and use it in GitHub Desktop.
Save Haroenv/d64495d439f3c28fcba8b983b8c9b2d7 to your computer and use it in GitHub Desktop.
React InstantSearch, but made with InstantSearch connectors & hooks
/* eslint-disable react/prop-types, require-await, no-console */
import ReactDOM from 'react-dom';
import React, {
useEffect,
useState,
useRef,
createContext,
useContext,
} from 'react';
import instantsearch from '../es';
import { connectSearchBox, connectHits } from '../es/connectors';
import isEqual from 'lodash/isEqual';
const InstanceContext = createContext();
function useConnector(connector, widgetParams) {
const instantSearchInstance = useContext(InstanceContext);
const [state, setState] = useState();
const widgetFactory = connector(
args => {
console.log('render', args.query);
setState(args);
},
() => {}
);
const previousParams = useRef();
useEffect(() => {
if (
instantSearchInstance &&
!isEqual(previousParams.current, widgetParams)
) {
previousParams.current = widgetParams;
const widget = widgetFactory(widgetParams);
instantSearchInstance.addWidget(widget);
console.log('added!', widget);
return () => instantSearchInstance.removeWidget(widget);
}
return undefined;
});
return state;
}
function SearchBox() {
const widgetParams = {};
const state = useConnector(connectSearchBox, widgetParams);
if (!state) {
return null;
}
console.log('state',state.query);
return (
<input
type="search"
value={state.query}
onChange={e => {
console.log('searchbox',e.target.value)
state.refine(e.target.value);
}}
/>
);
}
const DefaultResult = ({ result }) => <div>{result.name}</div>;
function Hits({ Result = DefaultResult }) {
const widgetParams = {};
const state = useConnector(connectHits, widgetParams);
if (!state) {
return null;
}
return (
<ol>
{state.hits.map(item => (
<li key={item.objectId}>
<Result result={item} />
</li>
))}
</ol>
);
}
function InstantSearch({ searchClient, indexName, children }) {
const [instantSearchInstance, setInstantSearchInstance] = useState();
useEffect(() => {
const instance = instantsearch({
indexName,
searchClient,
});
instance.start();
setInstantSearchInstance(instance);
return () => instance.dispose();
}, [searchClient, indexName]);
return (
<InstanceContext.Provider value={instantSearchInstance}>
{children}
</InstanceContext.Provider>
);
}
const searchClient = {
search: async ([{ params }]) => {
console.log('search', params.query);
return {
results: [
{
hits: [{ objectId: 'hi', name: `hi, ${params.query}` }],
query: params.query,
},
],
}
},
};
import { storiesOf } from '@storybook/html';
storiesOf('dumbstory', module).add('default', () => {
const el = document.createElement('div');
ReactDOM.render(
<InstantSearch searchClient={searchClient} indexName="whatever">
<SearchBox />
<Hits />
</InstantSearch>,
el
);
return el;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment