Skip to content

Instantly share code, notes, and snippets.

@swyxio
Last active July 26, 2022 10:43
Show Gist options
  • Save swyxio/efd9ee71669413bca6a895d87e30742f to your computer and use it in GitHub Desktop.
Save swyxio/efd9ee71669413bca6a895d87e30742f to your computer and use it in GitHub Desktop.
Sync your state to your query params for copy-pastable state-in-URLs. React Router, Gatsby, and Svelte versions
// note - this was my rough working prototype, but still left some artifacts in the query params.
// todo: also need to debounce the history pushes
// see comments for production Gatsby and Svelte versions which delete nondefault keys in the query params
import React from 'react'
import queryString from "query-string"
export const connectRouterState = defaultURLState => Component => props => {
const { history, location } = props
// note: do not nest objects in URL state
const urlState = { ...defaultURLState, ...queryString.parse(location.search) }
const setURLState = newState =>
history.push({ search: `?${queryString.stringify({ ...urlState, ...newState })}` })
return <Component
setURLState={setURLState}
urlState={urlState}
{...props}
/>
}
// usage example - assumes this connected component is provided `location` and `history` by react-router
class MyApp extends Component {
render() {
const { urlState, setURLState } = this.props
return (
<div>
<button onClick={() => setURLState({ newState: [1, 2] })}>{JSON.stringify(urlState)}</button>
</div>
)
}
}
export default connectRouterState({ defaultState: 1 })(MyApp)
@swyxio
Copy link
Author

swyxio commented Sep 23, 2020

my svelte code today

  import queryString from "query-string"
  import {onMount} from 'svelte'

  let urlState = {filter: '' }
  let defaultURLState = { filter: '' }
  onMount(() => {
    urlState = { ...defaultURLState, ...queryString.parse(location.search) }
  })
  
  const setURLState = newState => {
    const finalState = { ...urlState, ...newState } // merge with existing urlstate
    Object.keys(finalState).forEach(function (k) {
      if ( // don't save some state values if it meets the conditions below
        !finalState[k] || // falsy
        finalState[k] === "" || // string
        (Array.isArray(finalState[k]) && !finalState[k].length) || // array
        finalState[k] === defaultURLState[k] // same as default state, unnecessary
      ) {
        delete finalState[k]; // drop query params with new values = falsy
      }
    });
    urlState = finalState // sync queryparam state to svelte component state
    if (typeof window !== 'undefined') history.pushState({},'', document.location.origin + document.location.pathname + '?' + queryString.stringify(finalState))
  }
  let filterStr = ''
  $: setURLState({filter: filterStr })

  

TODO: Debounce

@swyxio
Copy link
Author

swyxio commented Sep 23, 2020

can apply this debounce code but it makes the state update a little slow

  function debounce(func, wait = 200) {
    var timeout;
    return function executedFunction() {
      var context = this;
      var args = arguments;
      var later = function() {
        timeout = null;
      };
      var callNow = !timeout;
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);
      if (callNow) func.apply(context, args);
    };
  };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment