Skip to content

Commit 3e1b3d1

Browse files
committed
fix: Implement hooks
1 parent cebdbb4 commit 3e1b3d1

File tree

9 files changed

+442
-0
lines changed

9 files changed

+442
-0
lines changed

src/declaration.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
declare module "parse/node.js" {
2+
import Parse from "parse/node.js";
3+
export = Parse;
4+
}

src/hooks/useObjectState.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import React from "react";
2+
3+
type Result<T> = [T, (value: Partial<T>) => void, () => void];
4+
5+
export function useObjectState<T>(initialState: T): Result<T> {
6+
const [state, setState] = React.useState<T>(initialState);
7+
8+
const set = React.useCallback(
9+
(value: Partial<T>) => {
10+
setState((currentState: T) => {
11+
return { ...currentState, ...value };
12+
});
13+
},
14+
[setState]
15+
);
16+
17+
const reset = React.useCallback(() => {
18+
setState(initialState);
19+
}, [setState, initialState]);
20+
21+
return React.useMemo(() => [state, set, reset], [state, set, reset]);
22+
}

src/hooks/useParseFunction.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import Parse from "parse/node.js";
2+
import React from "react";
3+
4+
type Result<T> = {
5+
result: T | undefined;
6+
loading: boolean;
7+
error: Error | undefined;
8+
reload: () => void;
9+
};
10+
11+
export function useParseFunction<T>(
12+
name: string,
13+
params: any,
14+
dependency?: any
15+
): Result<T> {
16+
const [state, setState] = React.useState<Result<T>>({
17+
result: undefined,
18+
loading: true,
19+
error: undefined,
20+
reload: fetch,
21+
});
22+
23+
async function fetch() {
24+
if (!name) {
25+
return;
26+
}
27+
28+
Parse.Cloud.run(name, params).then(
29+
(response: any) => {
30+
setState({
31+
result: response,
32+
error: undefined,
33+
loading: false,
34+
reload: fetch,
35+
});
36+
},
37+
(error: Error) => {
38+
setState({
39+
result: undefined,
40+
error,
41+
loading: false,
42+
reload: fetch,
43+
});
44+
}
45+
);
46+
}
47+
48+
React.useEffect(() => {
49+
setState({
50+
result: undefined,
51+
loading: true,
52+
error: undefined,
53+
reload: fetch,
54+
});
55+
56+
fetch();
57+
}, [name, dependency]);
58+
59+
return state;
60+
}

src/hooks/useParseGet.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import Parse from "parse/node.js";
2+
import React from "react";
3+
4+
type Result<T> = {
5+
result: T | undefined;
6+
loading: boolean;
7+
error: Error | undefined;
8+
reload: () => void;
9+
};
10+
11+
export function useParseGet<T extends Parse.Object>(
12+
cls: new (...args: any[]) => T,
13+
id: string,
14+
options?: { includes?: string[] }
15+
): Result<T> {
16+
const [state, setState] = React.useState<Result<T>>({
17+
result: undefined,
18+
loading: true,
19+
error: undefined,
20+
reload: fetch,
21+
});
22+
23+
async function fetch() {
24+
if (cls && id) {
25+
const query = new Parse.Query(cls as Parse.Object);
26+
27+
if (options?.includes) {
28+
for (const include of options.includes) {
29+
query.include(include);
30+
}
31+
}
32+
33+
try {
34+
setState({
35+
result: (await query.get(id)) as T,
36+
loading: false,
37+
error: undefined,
38+
reload: fetch,
39+
});
40+
} catch (error) {
41+
setState({
42+
result: undefined,
43+
error: error as Error,
44+
loading: false,
45+
reload: fetch,
46+
});
47+
}
48+
}
49+
}
50+
51+
React.useEffect(() => {
52+
setState({
53+
result: undefined,
54+
loading: true,
55+
error: undefined,
56+
reload: fetch,
57+
});
58+
59+
fetch();
60+
}, [cls, id]);
61+
62+
return state;
63+
}

src/hooks/useParseList.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import Parse from "parse/node.js";
2+
import React from "react";
3+
4+
type Result<T> = {
5+
result: T[];
6+
count: number;
7+
loading: boolean;
8+
error: Error | undefined;
9+
reload: () => void;
10+
};
11+
12+
export function useParseList<T extends Parse.Object>(
13+
cls: new (...args: any[]) => T,
14+
options?: { count?: boolean; includes?: string[] }
15+
): Result<T> {
16+
const [state, setState] = React.useState<Result<T>>({
17+
result: [],
18+
count: 0,
19+
loading: true,
20+
error: undefined,
21+
reload: fetch,
22+
});
23+
24+
async function fetch() {
25+
if (cls) {
26+
const query = new Parse.Query(cls);
27+
28+
query.descending("updatedAt");
29+
30+
if (options?.includes) {
31+
for (const include of options.includes) {
32+
query.include(include);
33+
}
34+
}
35+
36+
try {
37+
setState({
38+
result: await query.find(),
39+
count: options?.count ? await query.count() : 0,
40+
error: undefined,
41+
loading: false,
42+
reload: fetch,
43+
});
44+
} catch (error) {
45+
setState({
46+
result: [],
47+
count: 0,
48+
error: error as Error,
49+
loading: false,
50+
reload: fetch,
51+
});
52+
}
53+
}
54+
}
55+
56+
React.useEffect(() => {
57+
setState({
58+
result: [],
59+
count: 0,
60+
loading: true,
61+
error: undefined,
62+
reload: fetch,
63+
});
64+
65+
fetch();
66+
}, [cls]);
67+
68+
return state;
69+
}

src/hooks/useParseQuery.ts

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import Parse from "parse/node.js";
2+
import React from "react";
3+
4+
type Result<T> = {
5+
result: T[];
6+
count: number;
7+
loading: boolean;
8+
error: Error | undefined;
9+
reload: () => void;
10+
};
11+
12+
export function useParseQuery<T extends Parse.Object>(
13+
query: Parse.Query<T>,
14+
options?: {
15+
count?: boolean;
16+
live?: boolean;
17+
liveReload?: boolean;
18+
reloadThrottle?: number;
19+
}
20+
): Result<T> {
21+
const subscriptionRef = React.useRef<Parse.LiveQuerySubscription>();
22+
const timeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>();
23+
const throttleRef = React.useRef<boolean>(false);
24+
25+
const initialState = {
26+
result: [],
27+
count: 0,
28+
loading: true,
29+
error: undefined,
30+
reload: fetch,
31+
};
32+
33+
const [state, setState] = React.useState<Result<T>>(initialState);
34+
35+
async function fetch(force: boolean = false) {
36+
if (
37+
!force &&
38+
options?.reloadThrottle &&
39+
Number.isInteger(options?.reloadThrottle)
40+
) {
41+
if (timeoutRef.current) {
42+
throttleRef.current = true;
43+
return;
44+
} else {
45+
timeoutRef.current = setTimeout(() => {
46+
timeoutRef.current = null;
47+
48+
if (throttleRef.current) {
49+
fetch();
50+
}
51+
52+
throttleRef.current = false;
53+
}, options.reloadThrottle);
54+
}
55+
}
56+
57+
if (query) {
58+
if (options?.live) {
59+
if (subscriptionRef.current) {
60+
await subscriptionRef.current.unsubscribe();
61+
}
62+
63+
subscriptionRef.current = await query.subscribe();
64+
65+
// subscriptionRef.current.on("open", () => {});
66+
67+
subscriptionRef.current.on("create", (object: T) => {
68+
if (options?.liveReload) {
69+
fetch();
70+
} else {
71+
setState(mergeResult(object, true));
72+
}
73+
});
74+
75+
subscriptionRef.current.on("update", (object: T) => {
76+
if (options?.liveReload) {
77+
fetch();
78+
} else {
79+
setState(mergeResult(object, true));
80+
}
81+
});
82+
83+
subscriptionRef.current.on("enter", (object: T) => {
84+
if (options?.liveReload) {
85+
fetch();
86+
} else {
87+
setState(mergeResult(object, true));
88+
}
89+
});
90+
91+
subscriptionRef.current.on("leave", (object: T) => {
92+
if (options?.liveReload) {
93+
fetch();
94+
} else {
95+
setState(mergeResult(object));
96+
}
97+
});
98+
}
99+
100+
const requests: [Promise<T[]>, Promise<number>] = [
101+
query.find(),
102+
options?.count ? query.count() : Promise.resolve(0),
103+
];
104+
105+
Promise.all(requests).then(
106+
([response, count]) => {
107+
if (Array.isArray(response)) {
108+
setState(
109+
mergeState({
110+
result: response,
111+
count: count || 0,
112+
loading: false,
113+
})
114+
);
115+
} else {
116+
setState(
117+
mergeState({ error: new Error("Bad Response"), loading: false })
118+
);
119+
}
120+
},
121+
(error: Error) => {
122+
setState(mergeState({ error, loading: false }));
123+
}
124+
);
125+
}
126+
}
127+
128+
function mergeState(
129+
nextState: Partial<Result<T>>
130+
): (state: Result<T>) => Result<T> {
131+
return (state) => Object.assign({}, state, nextState);
132+
}
133+
134+
function mergeResult(
135+
object: T,
136+
add: boolean = false
137+
): (state: Result<T>) => Result<T> {
138+
return (state) => {
139+
const result = state.result.filter((e) => e.id !== object.id);
140+
141+
if (add) {
142+
result.push(object);
143+
}
144+
145+
return Object.assign({}, state, { result });
146+
};
147+
}
148+
149+
React.useEffect(() => {
150+
setState(initialState);
151+
fetch(true);
152+
153+
return () => {
154+
if (subscriptionRef.current) {
155+
subscriptionRef.current.unsubscribe();
156+
}
157+
};
158+
}, [query]);
159+
160+
return state;
161+
}

0 commit comments

Comments
 (0)