zustand-querystring 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,54 +7,27 @@ Try on [StackBlitz](https://stackblitz.com/github/nitedani/zustand-querystring/t
7
7
  Quickstart:
8
8
  ```ts
9
9
  import create from "zustand";
10
- import { immer } from "zustand/middleware/immer";
11
10
  import { querystring } from "zustand-querystring";
12
11
 
13
12
  interface Store {
14
13
  count: number;
15
- incrementCount: () => void;
16
-
17
14
  ticks: number;
18
- incrementTicks: () => void;
19
-
20
15
  someNestedState: {
21
16
  nestedCount: number;
22
- incrementNestedCount: () => void;
23
-
24
17
  hello: string;
25
- setHello: (hello: string) => void;
26
18
  };
27
19
  }
28
20
 
29
21
  export const useStore = create<Store>()(
30
22
  querystring(
31
- immer((set, get) => ({
23
+ (set, get) => ({
32
24
  count: 0,
33
- incrementCount: () =>
34
- set((state) => {
35
- state.count += 1;
36
- }),
37
-
38
25
  ticks: 0,
39
- incrementTicks: () =>
40
- set((state) => {
41
- state.ticks += 1;
42
- }),
43
-
44
26
  someNestedState: {
45
27
  nestedCount: 0,
46
- incrementNestedCount: () =>
47
- set((state) => {
48
- state.someNestedState.nestedCount += 1;
49
- }),
50
-
51
28
  hello: "Hello",
52
- setHello: (hello: string) =>
53
- set((state) => {
54
- state.someNestedState.hello = hello;
55
- }),
56
29
  },
57
- })),
30
+ }),
58
31
  {
59
32
  // select controls what part of the state is synced with the query string
60
33
  // pathname is the current route (e.g. /about or /)
@@ -5,6 +5,7 @@ type DeepSelect<T> = T extends object ? {
5
5
  export interface QueryStringOptions<T> {
6
6
  url?: string;
7
7
  select?: (pathname: string) => DeepSelect<T>;
8
+ key?: string;
8
9
  }
9
10
  type QueryString = <T, Mps extends [StoreMutatorIdentifier, unknown][] = [], Mcs extends [StoreMutatorIdentifier, unknown][] = []>(initializer: StateCreator<T, Mps, Mcs>, options?: QueryStringOptions<T>) => StateCreator<T, Mps, Mcs>;
10
11
  export declare const querystring: QueryString;
package/lib/middleware.js CHANGED
@@ -24,6 +24,10 @@ const compact = (newState, initialState) => {
24
24
  return output;
25
25
  };
26
26
  const translateSelectionToState = (selection, state) => Object.keys(selection).reduce((acc, key) => {
27
+ // @ts-ignore
28
+ if (!(key in state)) {
29
+ return acc;
30
+ }
27
31
  const value = selection[key];
28
32
  if (typeof value === 'boolean') {
29
33
  if (value) {
@@ -38,28 +42,41 @@ const translateSelectionToState = (selection, state) => Object.keys(selection).r
38
42
  const queryStringImpl = (fn, options) => (set, get, api) => {
39
43
  const defaultedOptions = {
40
44
  partialize: state => state,
45
+ key: '$',
41
46
  ...options,
42
47
  };
43
48
  const url = defaultedOptions.url;
44
49
  const initialState = get() ?? fn(set, get, api);
45
- const getSelectedState = () => {
50
+ const getSelectedState = (state, pathname) => {
46
51
  if (defaultedOptions.select) {
47
- const selection = defaultedOptions.select(window.location.pathname);
52
+ const selection = defaultedOptions.select(pathname);
48
53
  // translate the selection to state
49
- const selectedState = translateSelectionToState(selection, get());
54
+ const selectedState = translateSelectionToState(selection, state);
50
55
  return selectedState;
51
56
  }
52
- return get();
57
+ return state;
53
58
  };
54
59
  const initialize = (url, _set = set) => {
60
+ const fallback = () => fn(_set, get, api);
55
61
  try {
56
- const queryString = url.split('?')[1]?.slice(2);
62
+ const splitUrl = url.split('?');
63
+ const queryString = splitUrl[1];
64
+ const pathname = splitUrl[0];
57
65
  if (!queryString) {
58
- return fn(_set, get, api);
66
+ return fallback();
67
+ }
68
+ const idx = queryString.indexOf(defaultedOptions.key + '=');
69
+ if (idx === -1) {
70
+ return fallback();
59
71
  }
60
- const parsed = parse(queryString);
72
+ const toParse = queryString.substring(idx + 2);
73
+ if (!toParse) {
74
+ return fallback();
75
+ }
76
+ console.log('toParse', toParse);
77
+ const parsed = parse(toParse);
61
78
  const currentValue = get() ?? fn(_set, get, api);
62
- const merged = mergeWith(currentValue, parsed);
79
+ const merged = mergeWith(currentValue, getSelectedState(parsed, pathname));
63
80
  set(merged, true);
64
81
  return merged;
65
82
  }
@@ -73,27 +90,43 @@ const queryStringImpl = (fn, options) => (set, get, api) => {
73
90
  };
74
91
  if (typeof window !== 'undefined') {
75
92
  const setQuery = () => {
76
- const selectedState = getSelectedState();
93
+ const selectedState = getSelectedState(get(), window.location.pathname);
77
94
  if (!selectedState) {
78
95
  return;
79
96
  }
80
97
  const compactedSelectedState = compact(selectedState, initialState);
81
- if (!compactedSelectedState) {
82
- window.history.replaceState(null, '', window.location.pathname);
83
- return;
84
- }
85
- const stringified = stringify(compactedSelectedState);
86
- if (stringified) {
98
+ const stringified = compactedSelectedState && stringify(compactedSelectedState);
99
+ const currentQueryString = window.location.search.slice(1);
100
+ if (stringified || currentQueryString) {
87
101
  // console.log('set query', stringified);
88
102
  // console.log('parse query', parse(stringified));
89
- window.history.replaceState(null, '', `?q=${stringified}`);
103
+ // parse current querystring
104
+ // split query string to key-value
105
+ const variables = {};
106
+ const currentQuery = currentQueryString.split('&');
107
+ for (const variable of currentQuery) {
108
+ if (stringified?.includes(variable)) {
109
+ continue;
110
+ }
111
+ const [key, value] = variable.split('=');
112
+ variables[key] = value;
113
+ }
114
+ variables[defaultedOptions.key] = stringified;
115
+ const newQueryString = Object.keys(variables) // filter out empty values
116
+ .filter(key => variables[key])
117
+ // join key-value pairs
118
+ .map(key => `${key}=${variables[key]}`)
119
+ // join all pairs with &
120
+ .join('&');
121
+ console.log('newQueryString', newQueryString);
122
+ window.history.replaceState(null, '', newQueryString ? `?${newQueryString}` : window.location.pathname);
90
123
  }
91
124
  };
92
125
  //TODO: find a better way to do this
93
- let previousUrl = '';
126
+ let previousPathname = '';
94
127
  setInterval(() => {
95
- if (window.location.href !== previousUrl) {
96
- previousUrl = window.location.href;
128
+ if (window.location.pathname !== previousPathname) {
129
+ previousPathname = window.location.pathname;
97
130
  setQuery();
98
131
  }
99
132
  }, 50);
@@ -102,7 +135,7 @@ const queryStringImpl = (fn, options) => (set, get, api) => {
102
135
  originalSetState(...args);
103
136
  setQuery();
104
137
  };
105
- return initialize(window.location.href, (...args) => {
138
+ return initialize(window.location.pathname + window.location.search, (...args) => {
106
139
  set(...args);
107
140
  setQuery();
108
141
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zustand-querystring",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "keywords": [