state-in-url
Version:
Store state in URL as in object, types and structure are preserved, with TS validation. Same API as React.useState, wthout any hasssle or boilerplate. Next.js@14-15, react-router@6-7, and remix@2.
2 lines (1 loc) • 1.5 kB
JavaScript
import S from"react";import{TIMEOUT as y}from"../constants/constants.mjs";import{useInsertionEffect as L}from"../useInsertionEffect.mjs";import{useSharedState as P}from"../useSharedState/useSharedState.mjs";import{useUrlEncode as T}from"../useUrlEncode/useUrlEncode.mjs";import{popstateEv as U,filterUnknownParamsClient as b,getSearch as C}from"../utils.mjs";let i;let n;function R(t,o,l,u){const{parse:r,stringify:s}=T(t);const{state:d,getState:c,setState:a}=P(t,()=>l?.({parse:r})||t);L(()=>{const e=()=>{a(r(b(t,C())))};window.addEventListener(U,e);return()=>{window.removeEventListener(U,e)}},[]);const p=S.useCallback((e,v)=>{const m=typeof e==="function"?e(c(),t):{...c(),...e};const f=s(m,I(t));const w=O(window.location.pathname,u);const h=`${w}${f.length?"?":""}${f}${window.location.hash}`;const g=`${w}${window.location.search}${window.location.hash}`;if(h===g)return;a(m);const{replace:E,...k}=v||{};clearTimeout(i);i=void 0;n={router:o,method:E?"replace":"push",url:h,opts:k};i=setTimeout(()=>{if(n){n.router[n.method](n.url,n.opts);n=void 0}i=void 0},y)},[o,s,c]);const $=S.useCallback(e=>{a(t);p(t,e)},[p,a]);return{updateState:a,updateUrl:p,state:d,reset:$,getState:c,pendingUrlUpdate:()=>i!==void 0}}function I(t){const o=Object.keys(t);const l=window.location.search;const u=new URLSearchParams(l);const r=new URLSearchParams;for(const[s,d]of u){!o.includes(s)&&r.set(s,d)}return r}function O(t,o){if(!o||o==="/"){return t}return t.slice(o.length)}export{R as useUrlStateBase};