mirror of
https://github.com/myfatemi04/wheelshare-frontend.git
synced 2025-04-16 00:50:18 -04:00
add useImmutable
This commit is contained in:
parent
d276d02cc4
commit
9e47c562a5
97
src/components/useImmutable.ts
Normal file
97
src/components/useImmutable.ts
Normal file
|
@ -0,0 +1,97 @@
|
|||
import { Dispatch, SetStateAction, useMemo, useState } from 'react';
|
||||
|
||||
type Primitive = bigint | boolean | null | number | string | symbol | undefined;
|
||||
|
||||
type PlainJS = Primitive | PlainJSObject | PlainJSArray;
|
||||
|
||||
interface PlainJSObject {
|
||||
[key: string]: PlainJS;
|
||||
}
|
||||
|
||||
interface PlainJSArray<Item extends PlainJS = PlainJS> extends Array<Item> {}
|
||||
|
||||
function createEdgeForObject<T extends PlainJSObject>(
|
||||
value: T,
|
||||
setValue: Dispatch<SetStateAction<T>>
|
||||
): T {
|
||||
const keys = Object.keys(value);
|
||||
// @ts-expect-error
|
||||
const edge: T = {};
|
||||
for (let key of keys) {
|
||||
Object.defineProperty(edge, key, {
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
get: () => value[key],
|
||||
set: (v) => void setValue((value) => ({ ...value, [key]: v })),
|
||||
});
|
||||
}
|
||||
return edge;
|
||||
}
|
||||
|
||||
function createEdgeForArray<T extends PlainJS>(
|
||||
value: PlainJSArray<T>,
|
||||
setValue: Dispatch<SetStateAction<PlainJSArray<T>>>
|
||||
) {
|
||||
return new Proxy(value, {
|
||||
set: (target, property, value) => {
|
||||
if (typeof property === 'number') {
|
||||
const set = (next: SetStateAction<T>) => {
|
||||
const v = typeof next === 'function' ? next(value) : next;
|
||||
const edge = createEdge(v, set);
|
||||
setValue((v) => [
|
||||
...v.slice(0, property),
|
||||
edge,
|
||||
...v.slice(property + 1),
|
||||
]);
|
||||
};
|
||||
|
||||
set(value);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
// @ts-expect-error
|
||||
get: (target, property: keyof PlainJSArray<T>[]) => {
|
||||
if (typeof property === 'number') {
|
||||
return target[property];
|
||||
} else {
|
||||
if (typeof target[property] === 'function') {
|
||||
return function () {
|
||||
const newValue = [...value];
|
||||
const method = newValue[property];
|
||||
// @ts-expect-error
|
||||
const result = method.apply(newValue, arguments);
|
||||
setValue(newValue);
|
||||
return result;
|
||||
};
|
||||
} else {
|
||||
return target[property];
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function createEdge<T extends PlainJS>(
|
||||
value: T,
|
||||
setValue: Dispatch<SetStateAction<T>>
|
||||
): T {
|
||||
if (Array.isArray(value)) {
|
||||
// @ts-expect-error
|
||||
return createEdgeForArray(value, setValue) as T;
|
||||
} else if (typeof value === 'object' && value !== null) {
|
||||
// @ts-expect-error
|
||||
return createEdgeForObject(value, setValue) as T;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
export default function useImmutable<T extends PlainJS>(
|
||||
initial: T
|
||||
): [T, Dispatch<SetStateAction<T>>] {
|
||||
const [value, setValue] = useState(initial);
|
||||
|
||||
const edge = useMemo(() => createEdge(value, setValue), [value]);
|
||||
|
||||
return [edge, setValue];
|
||||
}
|
Loading…
Reference in New Issue
Block a user