zustand-querystring 0.0.10 → 0.0.12
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 +12 -2
- package/lib/middleware.js +54 -23
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
# zustand-querystring
|
|
2
2
|
|
|
3
|
-
A zustand middleware that
|
|
3
|
+
A zustand middleware that syncs state with the querystring.
|
|
4
4
|
|
|
5
5
|
Try on [StackBlitz](https://stackblitz.com/github/nitedani/zustand-querystring/tree/main/examples/react) (You need to click "Open in New Tab")
|
|
6
6
|
|
|
7
|
+
Examples:
|
|
8
|
+
- [React](./examples/react/)
|
|
9
|
+
- [NextJS](./examples/next/)
|
|
10
|
+
- [Rakkas](./example/rakkas/)
|
|
11
|
+
|
|
7
12
|
Quickstart:
|
|
8
13
|
```ts
|
|
9
14
|
import create from "zustand";
|
|
@@ -48,4 +53,9 @@ export const useStore = create<Store>()(
|
|
|
48
53
|
}
|
|
49
54
|
)
|
|
50
55
|
);
|
|
51
|
-
```
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
querystring options:
|
|
59
|
+
- <b>select</b> - the select option controls what part of the state is synced with the query string
|
|
60
|
+
- <b>key: string</b> - the key option controls how the state is stored in the querystring (default: $)
|
|
61
|
+
- <b>url</b> - the url option is used to provide the request url on the server side render
|
package/lib/middleware.js
CHANGED
|
@@ -47,9 +47,10 @@ const queryStringImpl = (fn, options) => (set, get, api) => {
|
|
|
47
47
|
key: '$',
|
|
48
48
|
...options,
|
|
49
49
|
};
|
|
50
|
-
const
|
|
50
|
+
const stateMatcher = new RegExp(`${escapeStringRegexp(defaultedOptions.key)}=(.*);;`);
|
|
51
|
+
const splitMatcher = new RegExp(`${escapeStringRegexp(defaultedOptions.key)}=.*;;`);
|
|
51
52
|
const parseQueryString = querystring => {
|
|
52
|
-
const match = querystring.match(
|
|
53
|
+
const match = querystring.match(stateMatcher);
|
|
53
54
|
if (match) {
|
|
54
55
|
let m = match[1];
|
|
55
56
|
if (!m.startsWith('$')) {
|
|
@@ -73,13 +74,15 @@ const queryStringImpl = (fn, options) => (set, get, api) => {
|
|
|
73
74
|
const initialize = (url, _set = set) => {
|
|
74
75
|
const fallback = () => fn(_set, get, api);
|
|
75
76
|
try {
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
const pathname = splitUrl[0];
|
|
77
|
+
const queryString = url.search.substring(1);
|
|
78
|
+
const pathname = url.pathname;
|
|
79
79
|
if (!queryString) {
|
|
80
80
|
return fallback();
|
|
81
81
|
}
|
|
82
82
|
const parsed = parseQueryString(queryString);
|
|
83
|
+
if (!parsed) {
|
|
84
|
+
return fallback();
|
|
85
|
+
}
|
|
83
86
|
const currentValue = get() ?? fn(_set, get, api);
|
|
84
87
|
const merged = mergeWith(currentValue, getSelectedState(parsed, pathname));
|
|
85
88
|
set(merged, true);
|
|
@@ -92,45 +95,73 @@ const queryStringImpl = (fn, options) => (set, get, api) => {
|
|
|
92
95
|
};
|
|
93
96
|
if (typeof window !== 'undefined') {
|
|
94
97
|
const setQuery = () => {
|
|
95
|
-
const selectedState = getSelectedState(get(),
|
|
96
|
-
const currentQueryString =
|
|
98
|
+
const selectedState = getSelectedState(get(), location.pathname);
|
|
99
|
+
const currentQueryString = location.search;
|
|
97
100
|
const currentParsed = parseQueryString(currentQueryString);
|
|
98
101
|
const newMerged = {
|
|
99
102
|
...currentParsed,
|
|
100
103
|
...selectedState,
|
|
101
104
|
};
|
|
105
|
+
const splitIgnored = currentQueryString.split(splitMatcher);
|
|
106
|
+
let ignored = '';
|
|
107
|
+
for (let str of splitIgnored) {
|
|
108
|
+
if (!str || str === '?' || str === '&') {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (str.startsWith('&') || str.startsWith('?')) {
|
|
112
|
+
str = str.substring(1);
|
|
113
|
+
}
|
|
114
|
+
if (str.endsWith('&')) {
|
|
115
|
+
str = str.substring(0, str.length - 1);
|
|
116
|
+
}
|
|
117
|
+
ignored += (ignored ? '&' : '?') + str;
|
|
118
|
+
}
|
|
102
119
|
const newCompacted = compact(newMerged, initialState);
|
|
103
120
|
if (Object.keys(newCompacted).length) {
|
|
104
121
|
const stringified = stringify(newCompacted).substring(1);
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
122
|
+
const newQueryState = `${defaultedOptions.key}=${stringified};;`;
|
|
123
|
+
let newQueryString = '';
|
|
124
|
+
if (currentParsed) {
|
|
125
|
+
newQueryString = currentQueryString.replace(splitMatcher, newQueryState);
|
|
126
|
+
}
|
|
127
|
+
else if (ignored) {
|
|
128
|
+
newQueryString = ignored + '&' + newQueryState;
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
newQueryString = '?' + newQueryState;
|
|
132
|
+
}
|
|
133
|
+
history.replaceState(history.state, '', location.pathname + newQueryString);
|
|
109
134
|
}
|
|
110
135
|
else {
|
|
111
|
-
|
|
136
|
+
history.replaceState(history.state, '', location.pathname + ignored);
|
|
112
137
|
}
|
|
113
138
|
};
|
|
114
|
-
//
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
139
|
+
// @ts-ignore
|
|
140
|
+
if (!api.__ZUSTAND_QUERYSTRING_INIT__) {
|
|
141
|
+
// @ts-ignore
|
|
142
|
+
api.__ZUSTAND_QUERYSTRING_INIT__ = true;
|
|
143
|
+
let previousPathname = '';
|
|
144
|
+
const cb = () => {
|
|
145
|
+
if (location.pathname !== previousPathname) {
|
|
146
|
+
previousPathname = location.pathname;
|
|
147
|
+
setQuery();
|
|
148
|
+
}
|
|
149
|
+
requestAnimationFrame(cb);
|
|
150
|
+
};
|
|
151
|
+
requestAnimationFrame(cb);
|
|
152
|
+
}
|
|
122
153
|
const originalSetState = api.setState;
|
|
123
154
|
api.setState = (...args) => {
|
|
124
155
|
originalSetState(...args);
|
|
125
156
|
setQuery();
|
|
126
157
|
};
|
|
127
|
-
return initialize(
|
|
158
|
+
return initialize(new URL(location.href), (...args) => {
|
|
128
159
|
set(...args);
|
|
129
160
|
setQuery();
|
|
130
161
|
});
|
|
131
162
|
}
|
|
132
|
-
if (url) {
|
|
133
|
-
return initialize(url);
|
|
163
|
+
else if (url) {
|
|
164
|
+
return initialize(new URL(decodeURIComponent(url), 'http://localhost'));
|
|
134
165
|
}
|
|
135
166
|
return fn(set, get, api);
|
|
136
167
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zustand-querystring",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"@types/lodash-es": "^4.17.6",
|
|
36
36
|
"rimraf": "^3.0.2",
|
|
37
37
|
"typescript": "^4.9.3",
|
|
38
|
-
"zustand": "^4.
|
|
38
|
+
"zustand": "^4.3.2"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "rimraf lib && tsc -b"
|