Skip to content

Instantly share code, notes, and snippets.

@Imater
Last active March 14, 2022 07:57
Show Gist options
  • Save Imater/88505c8b2412681919da7cfb5b63e600 to your computer and use it in GitHub Desktop.
Save Imater/88505c8b2412681919da7cfb5b63e600 to your computer and use it in GitHub Desktop.
import { useLocation, useHistory } from 'react-router-dom';
import { useEffect, useState } from 'react';
import moment, { Moment } from 'moment';
const isValidDate = (d: any): boolean => {
return moment(d).isValid();
};
type CustomPropertyKey = boolean | number[] | string[] | Date | PropertyKey;
export interface QueryHookParams {
[key: string]: CustomPropertyKey;
}
export const getOnlyRealParams = <T extends QueryHookParams>(allParams: T, realParams: T): T => {
return Object.keys(realParams).reduce<T>((o, key) => ({ ...o, [key]: allParams[key] }), realParams);
};
export const getNullableParams = <T extends QueryHookParams>(params: T): T => {
return Object.keys(params).reduce<T>((o, key) => ({ ...o, [key]: null }), { } as T);
};
export const isParamsNotEmpty = <T extends QueryHookParams>(params: T): boolean => {
return Object.values(params)
.filter(value => (!Array.isArray(value) && value !== null) || (Array.isArray(value) && value.length > 0))
.length !== 0;
};
const getValue = (value: string): any => {
if (!Number.isNaN(+value)) {
return +value;
}
if (['true', 'false'].includes(value)) {
return value === 'true';
}
return value;
};
export const queryParamsToObject = <T> (searchParams: URLSearchParams, objectParams: T): T => {
searchParams.forEach((value: any, key: string) => {
if (Array.isArray(objectParams[key])) {
objectParams[key] = [...objectParams[key], getValue(value)];
} else {
objectParams[key] = getValue(value);
}
});
return objectParams;
};
// eslint-disable-next-line @typescript-eslint/ban-types
export const useSyncQueryParams = <T extends {}>(
initialQueryParams: T,
): [T, (params: T, updateHistory?: boolean) => void] => {
const [init, setInit] = useState(false);
const currentParams = new URLSearchParams(useLocation().search);
const history = useHistory();
const updateUrl = (values: QueryHookParams, updateHistory = true) => {
let paramsChanged = false;
Object.entries(values).map(([k, v]) => {
if (((Array.isArray(v) && v.length === 0) || v === null || v === undefined || v === '') && currentParams.has(k)) {
currentParams.delete(k);
paramsChanged = true;
} else if (v !== null && v !== undefined && v !== '') {
if (typeof v === 'string' && typeof +v !== 'number' && isValidDate(v) && isValidDate(currentParams.get(k))) {
if (new Date(v).toISOString() !== new Date(currentParams.get(k)).toISOString()) {
currentParams.set(k, new Date(v).toISOString());
paramsChanged = true;
}
} else if ((v.toString() !== currentParams.get(k)) || (Array.isArray(v) && v.sort() === currentParams.getAll(k).sort())) {
if (Array.isArray(v)) {
currentParams.delete(k);
for (const item of v) {
currentParams.append(k, item?.toString());
}
paramsChanged = true;
} else {
currentParams.set(k, v?.toString());
paramsChanged = true;
}
}
}
});
if (updateHistory && paramsChanged) {
history.push({
pathname: history.location.pathname,
search: currentParams.toString()
});
}
};
// initial hook
useEffect(() => {
setInit(true);
return () => {
setInit(false);
updateUrl(getNullableParams(initialQueryParams), false);
};
}, []);
// change query params hook
useEffect(() => {
if (init) {
updateUrl(initialQueryParams, true);
}
}, [...Object.values(currentParams)]);
return [
queryParamsToObject<T>(currentParams, { ...initialQueryParams }),
updateUrl,
];
};
export const useQueryParams = () => {
return new URLSearchParams(useLocation().search);
};
@Imater
Copy link
Author

Imater commented Mar 14, 2022

Использовать вот так:
const [queryFilter, setQueryFilter] = useSyncQueryParams<{search: string}>({ search: '' });

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