Browser "back" & "forward" button support with URL routing

I trying to get React Instantsearch’s URL routing to work with the browser’s “back” & “forward” buttons.

The problem is that the official example doesn’t handle this. (I coded it on my own and got it “working”, but then had an issue with 2x requests being made to Algolia each time a search was made. So I’m starting with official example again, but it doesn’t support browser’s “back” and “forward” buttons.)

GOAL:

  • URL routing working (this works)
  • Debounced search (this works)
  • Browser’s back & forward buttons should update the search input’s value & results (does not work) << PROBLEM

EXAMPLE:

TO REPRO:

  1. Type “Samsung”
  2. Type “Apple”
  3. Click “back” button.
    Expected result: to see “Apple” in the search input and Apple results.
    Actual results: “Samsung” remains in the search input & Samsung results remain shown.

How can I get the search input’s value & results to update when the browser’s “back” & “forward” buttons are pressed?

(My example is based of these Algolia examples:

You can use the getDerivedStateFromProps lifecycle method to handle this case. Since the sandbox makes use of React hooks, you can read the section “How do I implement getDerivedStateFromProps?” from the React docs.

Can you be more specific please?

I took my best stab at what I thought you might have had in mind: 1.) passed a ‘query’ prop to the ‘DebouncedSearch’ component in app.js, and 2.) implemented ‘getDerivedStateFromProps()’ in DebouncedSearch.
Good: The search input’s value updates from the query string when clicking the browser’s “back” and “forward”.
Bad: The results don’t update when clicking “back” or “forward”. If I try calling ‘refine()’ within ‘getDerivedStateFromProps’, it breaks the browser’s history entirely (described in the note on the sandbox).

Is this what you had in mind?

If it plays badly with the browser history, you can subscribe to the popstate event of the window object. We created a complete example and the code can be found on GitHub.

This solution is a HOC that passes searchState, onSearchStateChange and createURL to the component.

Unfortunately, the debounce in that example applies to how frequently the search is added to the browser history, but it does not debounce the search box, which is the goal here (working URL routing + debounced search box).

Doing so would require creating a custom component like in my earlier examples and then would bring me back to the same question of how to handle this within the DebouncedSearch custom component.

Could you suggest how to edit one of my DebouncedSearch component examples please?:

  1. 1st link: Minimal DebouncedSearch.jsx: https://codesandbox.io/s/sleepy-sammet-fe7ph (based on the example in the docs)
  2. Or, 2nd link: DebouncedSearch.jsx attempting to use getDerivedStateFromProps: https://codesandbox.io/s/practical-snyder-btp69

Hello @jbb ,

Here’s the example with a debounced searchbox and the synchronized browser history: https://codesandbox.io/s/vibrant-montalcini-53e0v

Here’s the few changes made to your codesandbox:

This will sync InstantSearch state with the URL, but since you want to debounce the search then we need to allow some “desync” for the searchbox value. For this we’ll use this pattern https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#alternative-1-reset-uncontrolled-component-with-an-id-prop to only sync the searchbox value when the query from the searchState update.

  • Pass mySearchState.query as a prop to the DebouncedSearchBox component
  • Reset component internal state only when this prop changes

However, note that we do not recommend you to debounce the search since it adds a delay to the interface and often results in a bad experience for the users (UI feeling slow and unresponsive). If you decide to remove the debounce then you can simply uncomment the SearchBox component in the example and use it instead of the DebouncedSearchBox one.

1 Like

Thank you Yannick! That example looks like it works as desired! I’ll work on implementing it in my app.

However, note that we do not recommend you to debounce the search since it adds a delay to the interface and often results in a bad experience for the users (UI feeling slow and unresponsive). If you decide to remove the debounce then you can simply uncomment the SearchBox component in the example and use it instead of the DebouncedSearchBox one.

I appreciate the extra advice. Your responses are always super helpful, like thinking ahead if I ended up wanting to remove the DebouncedSearchBox.

In my case, I expose so many refinement options to the user that I believe it will consume 10-15 operations per request to Algolia (to get facet counts), which would consume ~70-105 operations for a typical, 7-letter word search. I would certainly prefer a “search as you type” experience with no debouncing of the search query, but I don’t think it will be viable (cost wise) in my case. So I think I have to live with a DebouncedSearchBox. (Right now I’m also working to weed out my code issues where some of my custom components that use connectors cause 2x requests to Algolia when the UI element is changed to refine a value. So I’m trying to get a handle on a reasonable operation consumption.)