@cdxoo/react-router-url-search-params
Version:
Hook to access and update url search params in conjunction with react-router
459 lines (393 loc) • 14 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var react = require('react');
var reactRouter = require('react-router');
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
/**!
* url-search-params-polyfill
*
* @author Jerry Bendy (https://github.com/jerrybendy)
* @licence MIT
*/
(function(self) {
var nativeURLSearchParams = (function() {
// #41 Fix issue in RN
try {
if (self.URLSearchParams && (new self.URLSearchParams('foo=bar')).get('foo') === 'bar') {
return self.URLSearchParams;
}
} catch (e) {}
return null;
})(),
isSupportObjectConstructor = nativeURLSearchParams && (new nativeURLSearchParams({a: 1})).toString() === 'a=1',
// There is a bug in safari 10.1 (and earlier) that incorrectly decodes `%2B` as an empty space and not a plus.
decodesPlusesCorrectly = nativeURLSearchParams && (new nativeURLSearchParams('s=%2B').get('s') === '+'),
isSupportSize = nativeURLSearchParams && 'size' in nativeURLSearchParams.prototype,
__URLSearchParams__ = "__URLSearchParams__",
// Fix bug in Edge which cannot encode ' &' correctly
encodesAmpersandsCorrectly = nativeURLSearchParams ? (function() {
var ampersandTest = new nativeURLSearchParams();
ampersandTest.append('s', ' &');
return ampersandTest.toString() === 's=+%26';
})() : true,
prototype = URLSearchParamsPolyfill.prototype,
iterable = !!(self.Symbol && self.Symbol.iterator);
if (nativeURLSearchParams && isSupportObjectConstructor && decodesPlusesCorrectly && encodesAmpersandsCorrectly && isSupportSize) {
return;
}
/**
* Make a URLSearchParams instance
*
* @param {object|string|URLSearchParams} search
* @constructor
*/
function URLSearchParamsPolyfill(search) {
search = search || "";
// support construct object with another URLSearchParams instance
if (search instanceof URLSearchParams || search instanceof URLSearchParamsPolyfill) {
search = search.toString();
}
this [__URLSearchParams__] = parseToDict(search);
}
/**
* Appends a specified key/value pair as a new search parameter.
*
* @param {string} name
* @param {string} value
*/
prototype.append = function(name, value) {
appendTo(this [__URLSearchParams__], name, value);
};
/**
* Deletes the given search parameter, and its associated value,
* from the list of all search parameters.
*
* @param {string} name
*/
prototype['delete'] = function(name) {
delete this [__URLSearchParams__] [name];
};
/**
* Returns the first value associated to the given search parameter.
*
* @param {string} name
* @returns {string|null}
*/
prototype.get = function(name) {
var dict = this [__URLSearchParams__];
return this.has(name) ? dict[name][0] : null;
};
/**
* Returns all the values association with a given search parameter.
*
* @param {string} name
* @returns {Array}
*/
prototype.getAll = function(name) {
var dict = this [__URLSearchParams__];
return this.has(name) ? dict [name].slice(0) : [];
};
/**
* Returns a Boolean indicating if such a search parameter exists.
*
* @param {string} name
* @returns {boolean}
*/
prototype.has = function(name) {
return hasOwnProperty(this [__URLSearchParams__], name);
};
/**
* Sets the value associated to a given search parameter to
* the given value. If there were several values, delete the
* others.
*
* @param {string} name
* @param {string} value
*/
prototype.set = function set(name, value) {
this [__URLSearchParams__][name] = ['' + value];
};
/**
* Returns a string containg a query string suitable for use in a URL.
*
* @returns {string}
*/
prototype.toString = function() {
var dict = this[__URLSearchParams__], query = [], i, key, name, value;
for (key in dict) {
name = encode(key);
for (i = 0, value = dict[key]; i < value.length; i++) {
query.push(name + '=' + encode(value[i]));
}
}
return query.join('&');
};
// There is a bug in Safari 10.1 and `Proxy`ing it is not enough.
var useProxy = self.Proxy && nativeURLSearchParams && (!decodesPlusesCorrectly || !encodesAmpersandsCorrectly || !isSupportObjectConstructor || !isSupportSize);
var propValue;
if (useProxy) {
// Safari 10.0 doesn't support Proxy, so it won't extend URLSearchParams on safari 10.0
propValue = new Proxy(nativeURLSearchParams, {
construct: function (target, args) {
return new target((new URLSearchParamsPolyfill(args[0]).toString()));
}
});
// Chrome <=60 .toString() on a function proxy got error "Function.prototype.toString is not generic"
propValue.toString = Function.prototype.toString.bind(URLSearchParamsPolyfill);
} else {
propValue = URLSearchParamsPolyfill;
}
/*
* Apply polyfill to global object and append other prototype into it
*/
Object.defineProperty(self, 'URLSearchParams', {
value: propValue
});
var USPProto = self.URLSearchParams.prototype;
USPProto.polyfill = true;
// Fix #54, `toString.call(new URLSearchParams)` will return correct value when Proxy not used
if (!useProxy && self.Symbol) {
USPProto[self.Symbol.toStringTag] = 'URLSearchParams';
}
/**
*
* @param {function} callback
* @param {object} thisArg
*/
USPProto.forEach = USPProto.forEach || function(callback, thisArg) {
var dict = parseToDict(this.toString());
Object.getOwnPropertyNames(dict).forEach(function(name) {
dict[name].forEach(function(value) {
callback.call(thisArg, value, name, this);
}, this);
}, this);
};
/**
* Sort all name-value pairs
*/
USPProto.sort = USPProto.sort || function() {
var dict = parseToDict(this.toString()), keys = [], k, i, j;
for (k in dict) {
keys.push(k);
}
keys.sort();
for (i = 0; i < keys.length; i++) {
this['delete'](keys[i]);
}
for (i = 0; i < keys.length; i++) {
var key = keys[i], values = dict[key];
for (j = 0; j < values.length; j++) {
this.append(key, values[j]);
}
}
};
/**
* Returns an iterator allowing to go through all keys of
* the key/value pairs contained in this object.
*
* @returns {function}
*/
USPProto.keys = USPProto.keys || function() {
var items = [];
this.forEach(function(item, name) {
items.push(name);
});
return makeIterator(items);
};
/**
* Returns an iterator allowing to go through all values of
* the key/value pairs contained in this object.
*
* @returns {function}
*/
USPProto.values = USPProto.values || function() {
var items = [];
this.forEach(function(item) {
items.push(item);
});
return makeIterator(items);
};
/**
* Returns an iterator allowing to go through all key/value
* pairs contained in this object.
*
* @returns {function}
*/
USPProto.entries = USPProto.entries || function() {
var items = [];
this.forEach(function(item, name) {
items.push([name, item]);
});
return makeIterator(items);
};
if (iterable) {
USPProto[self.Symbol.iterator] = USPProto[self.Symbol.iterator] || USPProto.entries;
}
if (!USPProto.size) {
Object.defineProperty(USPProto, 'size', {
get: function () {
var dict = parseToDict(this.toString());
if (USPProto === this) {
throw new TypeError('Illegal invocation at URLSearchParams.invokeGetter')
}
return Object.keys(dict).reduce(function (prev, cur) {
return prev + dict[cur].length;
}, 0);
}
});
}
function encode(str) {
var replace = {
'!': '%21',
"'": '%27',
'(': '%28',
')': '%29',
'~': '%7E',
'%20': '+',
'%00': '\x00'
};
return encodeURIComponent(str).replace(/[!'\(\)~]|%20|%00/g, function(match) {
return replace[match];
});
}
function decode(str) {
return str
.replace(/[ +]/g, '%20')
.replace(/(%[a-f0-9]{2})+/ig, function(match) {
return decodeURIComponent(match);
});
}
function makeIterator(arr) {
var iterator = {
next: function() {
var value = arr.shift();
return {done: value === undefined, value: value};
}
};
if (iterable) {
iterator[self.Symbol.iterator] = function() {
return iterator;
};
}
return iterator;
}
function parseToDict(search) {
var dict = {};
if (typeof search === "object") {
// if `search` is an array, treat it as a sequence
if (isArray(search)) {
for (var i = 0; i < search.length; i++) {
var item = search[i];
if (isArray(item) && item.length === 2) {
appendTo(dict, item[0], item[1]);
} else {
throw new TypeError("Failed to construct 'URLSearchParams': Sequence initializer must only contain pair elements");
}
}
} else {
for (var key in search) {
if (search.hasOwnProperty(key)) {
appendTo(dict, key, search[key]);
}
}
}
} else {
// remove first '?'
if (search.indexOf("?") === 0) {
search = search.slice(1);
}
var pairs = search.split("&");
for (var j = 0; j < pairs.length; j++) {
var value = pairs [j],
index = value.indexOf('=');
if (-1 < index) {
appendTo(dict, decode(value.slice(0, index)), decode(value.slice(index + 1)));
} else {
if (value) {
appendTo(dict, decode(value), '');
}
}
}
}
return dict;
}
function appendTo(dict, name, value) {
var val = typeof value === 'string' ? value : (
value !== null && value !== undefined && typeof value.toString === 'function' ? value.toString() : JSON.stringify(value)
);
// #47 Prevent using `hasOwnProperty` as a property name
if (hasOwnProperty(dict, name)) {
dict[name].push(val);
} else {
dict[name] = [val];
}
}
function isArray(val) {
return !!val && '[object Array]' === Object.prototype.toString.call(val);
}
function hasOwnProperty(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
})(typeof commonjsGlobal !== 'undefined' ? commonjsGlobal : (typeof window !== 'undefined' ? window : commonjsGlobal));
const defaultParse = (queryRaw) => {
return queryRaw;
};
const defaultPrepare = (obj) => {
return obj;
};
const useURLSearchParams = (bag = {}) => {
let {
defaults,
parseReceived = defaultParse,
prepareUpdate = defaultPrepare,
} = bag;
let history = reactRouter.useHistory();
let location = reactRouter.useLocation();
let query = react.useMemo(() => {
let params = new URLSearchParams(location.search);
let paramsPOJO = {};
for (let key of params.keys()) {
paramsPOJO[key] = params.get(key);
}
var parsed = parseReceived(paramsPOJO);
return { ...defaults, ...parsed };
}, [ location ]);
let updateQuery = react.useCallback((obj, options = {}) => {
let {
// TODO deprecate 'push true' in favor of action = 'none'
push = true,
action = 'push',
mergedUpdate = false,
} = options;
if (mergedUpdate) {
obj = { ...query, ...obj };
}
var prepared = prepareUpdate(obj);
let stringified = (new URLSearchParams(prepared)).toString();
switch (action) {
case 'push':
if (push) {
history.push({
pathname: location.pathname,
search: stringified
});
}
break;
case 'replace':
history.replace({
pathname: location.pathname,
search: stringified
});
break;
case 'none':
break;
default:
throw new Error(`unknown action "${action}"`);
}
return stringified;
}, [ history, location ]);
return [ query, updateQuery ];
};
exports["default"] = useURLSearchParams;
exports.useURLSearchParams = useURLSearchParams;
//# sourceMappingURL=index.js.map