I am tickling with Algolia autocomplete, and I am trying to replicate their custom renderer in react using the class component. This is the sandbox of the minimal demo of custom renderer using functional component,
and here is my attempt to convert it into a class component.
import { createAutocomplete } from "@algolia/autocomplete-core"; import { getAlgoliaResults } from "@algolia/autocomplete-preset-algolia"; import algoliasearch from "algoliasearch/lite"; import React from "react"; const searchClient = algoliasearch( "latency", "6be0576ff61c053d5f9a3225e2a90f76" ); // let autocomplete; class AutocompleteClass extends React.PureComponent { constructor(props) { super(props); this.inputRef = React.createRef(); this.autocomplete = null; this.state = { autocompleteState: {}, }; } componentDidMount() { if (!this.inputRef.current) { return undefined; } this.autocomplete = createAutocomplete({ onStateChange({ state }) { // (2) Synchronize the Autocomplete state with the React state. this.setState({ autocompleteState: state }); }, getSources() { return [ { sourceId: "products", getItems({ query }) { return getAlgoliaResults({ searchClient, queries: [ { indexName: "instant_search", query, params: { hitsPerPage: 5, highlightPreTag: "<mark>", highlightPostTag: "</mark>", }, }, ], }); }, getItemUrl({ item }) { return item.url; }, }, ]; }, }); } render() { const { autocompleteState } = this.state; return ( <div className="aa-Autocomplete" {...this.autocomplete?.getRootProps({})}> <form className="aa-Form" {...this.autocomplete?.getFormProps({ inputElement: this.inputRef.current, })} > <div className="aa-InputWrapperPrefix"> <label className="aa-Label" {...this.autocomplete?.getLabelProps({})} > Search </label> </div> <div className="aa-InputWrapper"> <input className="aa-Input" ref={this.inputRef} {...this.autocomplete?.getInputProps({})} />componentDidUpdate() </div> </form> <div className="aa-Panel" {...this.autocomplete?.getPanelProps({})}> {autocompleteState.isOpen && autocompleteState.collections.map((collection, index) => { const { source, items } = collection; return ( <div key={`source-${index}`} className="aa-Source"> {items.length > 0 && ( <ul className="aa-List" {...this.autocomplete?.getListProps()} > {items.map((item) => ( <li key={item.objectID} className="aa-Item" {...this.autocomplete?.getItemProps({ item, source, })} > {item.name} </li> ))} </ul> )} </div> ); })} </div> </div> ); } } export default AutocompleteClass;
and the sandbox of the same version, I also tried using componentDidUpdate()
but no luck, any lead where I did wrong would be much appreciated thank you 🙂
Advertisement
Answer
Ok, dont know why you need it made into class component but here you go:
import { createAutocomplete } from "@algolia/autocomplete-core"; import { getAlgoliaResults } from "@algolia/autocomplete-preset-algolia"; import algoliasearch from "algoliasearch/lite"; import React from "react"; const searchClient = algoliasearch( "latency", "6be0576ff61c053d5f9a3225e2a90f76" ); // let autocomplete; class AutocompleteClass extends React.PureComponent { constructor(props) { super(props); this.state = { autocompleteState: {}, query: '', }; this.autocomplete = createAutocomplete({ onStateChange: this.onChange, getSources() { return [ { sourceId: "products", getItems({ query }) { console.log('getting query', query) return getAlgoliaResults({ searchClient, queries: [ { indexName: "instant_search", query, params: { hitsPerPage: 5, highlightPreTag: "<mark>", highlightPostTag: "</mark>" } } ] }); }, getItemUrl({ item }) { return item.url; } } ]; } }); } onChange = ({ state }) => { console.log(state) this.setState({ autocompleteState: state, query: state.query }); } render() { const { autocompleteState } = this.state; return ( <div className="aa-Autocomplete" {...this.autocomplete?.getRootProps({})}> <form className="aa-Form" {...this.autocomplete?.getFormProps({ inputElement: this.state.query })} > <div className="aa-InputWrapperPrefix"> <label className="aa-Label" {...this.autocomplete?.getLabelProps({})} > Search </label> </div> <div className="aa-InputWrapper"> <input className="aa-Input" value={this.state.query} {...this.autocomplete?.getInputProps({})} /> </div> </form> <div className="aa-Panel" {...this.autocomplete?.getPanelProps({})}> {autocompleteState.isOpen && autocompleteState.collections.map((collection, index) => { const { source, items } = collection; return ( <div key={`source-${index}`} className="aa-Source"> {items.length > 0 && ( <ul className="aa-List" {...this.autocomplete?.getListProps()} > {items.map((item) => ( <li key={item.objectID} className="aa-Item" {...this.autocomplete?.getItemProps({ item, source })} > {item.name} </li> ))} </ul> )} </div> ); })} </div> </div> ); } } export default AutocompleteClass;
Anyway the componentDidMount is called only once, and because of ref object is undefined it just returned from it.
Also messing with this
in class components is quite a bad idea (that is why func components are recommended)