Skip to content

Commit 8150588

Browse files
committedSep 26, 2019
feat(index): add refs type
1 parent 77f984c commit 8150588

File tree

2 files changed

+77
-47
lines changed

2 files changed

+77
-47
lines changed
 

‎declarations/index.d.ts

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
declare module ReactContextRefs {
2-
export interface Refs {}
2+
export interface Refs {
3+
myTest: { current: Element; meta: { hasError: boolean } };
4+
test2: { current: number; meta: { p: number } };
5+
[""]: { current: any; type: ""; meta: any };
6+
}
37

48
export type RefType = keyof Refs;
9+
10+
export type ValuesOf<T> = T[keyof T];
11+
12+
export type RefsValues<T> = {
13+
[K in keyof T]: T[K] extends { current: infer V; meta: infer M }
14+
? { type: K; current: V; meta: M }
15+
: never;
16+
};
17+
18+
export type RefsValuesMap = RefsValues<Refs>;
19+
20+
export type RefsData = ValuesOf<RefsValuesMap>;
521
}

‎src/index.tsx

+60-46
Original file line numberDiff line numberDiff line change
@@ -1,114 +1,128 @@
1+
///<reference path="../declarations/index.d.ts" />
12
import * as React from "react";
23

4+
type RefsData = ReactContextRefs.RefsData;
5+
36
const Context = React.createContext<RefsContext>({
4-
nodes: [],
7+
refs: [],
58
register: () => {},
69
setMeta: () => {}
710
});
811

912
interface RefsContext {
10-
nodes: RefData[];
11-
register: (key: any, ref?: RefData) => void;
13+
refs: RefsData[];
14+
register: (key: any, ref?: RefsData) => void;
1215
setMeta: (key: any, meta: any) => void;
1316
}
1417

15-
export interface RefData {
16-
value: any;
17-
meta: any;
18-
}
19-
2018
export const RefProvider: React.FC = ({ children }) => {
21-
const { current: refs } = React.useRef<Map<any, RefData>>(new Map());
19+
const { current: refsMap } = React.useRef<Map<any, RefsData>>(new Map());
2220

23-
const [nodes, setNodes] = React.useState<RefData[]>([]);
21+
const [refs, setRefs] = React.useState<RefsData[]>([]);
2422

2523
const register = React.useCallback<RefsContext["register"]>(
2624
(key, refData) => {
2725
if (refData == null) {
28-
if (refs.has(key)) {
29-
refs.delete(key);
30-
setNodes(Array.from(refs.values()));
26+
if (refsMap.has(key)) {
27+
refsMap.delete(key);
28+
setRefs(Array.from(refsMap.values()));
3129
}
3230
} else {
33-
const oldRefData = refs.get(key) || ({} as RefData);
31+
const oldRefData = refsMap.get(key) || ({} as RefsData);
3432
if (
35-
oldRefData.value !== refData.value ||
36-
oldRefData.meta !== refData.meta
33+
oldRefData.current !== refData.current ||
34+
oldRefData.meta !== refData.meta ||
35+
oldRefData.type !== refData.type
3736
) {
38-
refs.set(key, refData);
39-
setNodes(Array.from(refs.values()));
37+
refsMap.set(key, refData);
38+
setRefs(Array.from(refsMap.values()));
4039
}
4140
}
4241
},
43-
[setNodes]
42+
[setRefs]
4443
);
4544

4645
const setMeta = React.useCallback<RefsContext["setMeta"]>(
4746
(key, meta) => {
48-
if (!refs.has(key)) {
47+
if (!refsMap.has(key)) {
4948
return;
5049
}
51-
const refData = refs.get(key);
50+
const refData = refsMap.get(key);
5251
if (!refData) {
5352
return;
5453
}
5554
refData.meta = meta;
5655
},
57-
[setNodes]
56+
[setRefs]
5857
);
5958

6059
const context = React.useMemo(
6160
() => ({
62-
nodes,
61+
refs,
6362
register,
6463
setMeta
6564
}),
66-
[nodes, register]
65+
[refs, register]
6766
);
6867

6968
return <Context.Provider value={context}>{children}</Context.Provider>;
7069
};
7170

72-
export function useNodes() {
73-
return React.useContext(Context).nodes;
74-
}
71+
export function useRefs(): RefsData[];
72+
export function useRefs<T extends ReactContextRefs.RefType>(
73+
type: T
74+
): Array<ReactContextRefs.RefsValuesMap[T]>;
75+
export function useRefs<T extends ReactContextRefs.RefType>(type?: T) {
76+
const { refs } = React.useContext(Context);
77+
78+
if (typeof type === "undefined") {
79+
return refs;
80+
}
7581

76-
/** @todo переделать на getNodesByType ? */
77-
// export function useRefs(tag?: string) {
78-
// const { nodes } = React.useContext(Context);
82+
return refs.filter(ref => ref.type === type);
83+
}
7984

80-
// return React.useMemo(
81-
// () =>
82-
// tag
83-
// ? nodes.filter(node => node.tag === tag).map(node => node.value)
84-
// : nodes.map(node => node.value),
85-
// [nodes, tag]
86-
// );
87-
// }
85+
const useIsomorphicLayoutEffect =
86+
typeof window !== "undefined" &&
87+
typeof window.document !== "undefined" &&
88+
typeof window.document.createElement !== "undefined"
89+
? React.useLayoutEffect
90+
: React.useEffect;
91+
92+
export function useContextRef(meta?: any): (value: any) => void;
93+
export function useContextRef<T extends ReactContextRefs.RefType>(
94+
type: T,
95+
meta: ReactContextRefs.RefsValuesMap[T]["meta"]
96+
): (value: ReactContextRefs.RefsValuesMap[T]["current"]) => void;
97+
export function useContextRef(typeParam: any, metaParam?: any) {
98+
const type = typeof metaParam === "undefined" ? "" : typeParam;
99+
const meta = typeof metaParam === "undefined" ? typeParam : metaParam;
88100

89-
export function useSetRef(meta?: any) {
90101
const { current: key } = React.useRef({});
91102

92103
const { register, setMeta } = React.useContext(Context);
93104

94105
const callback = React.useCallback(
95-
node => {
96-
register(key, node ? { value: node, meta } : null);
106+
current => {
107+
register(key, current ? { current, meta, type } : null);
97108
},
98109
[register]
99110
);
100111

101-
React.useEffect(() => {
112+
useIsomorphicLayoutEffect(() => {
102113
setMeta(key, meta);
103114
}, [meta]);
104115

105116
return callback;
106117
}
107118

108-
export function ContextRef(props: {
109-
children: (ref: (value: any) => void) => React.ReactNode;
110-
meta?: any;
119+
export function ContextRef<T extends ReactContextRefs.RefType = "">(props: {
120+
children: (
121+
ref: (current: ReactContextRefs.RefsValuesMap[T]["current"]) => void
122+
) => React.ReactNode;
123+
type?: T;
124+
meta?: ReactContextRefs.RefsValuesMap[T]["meta"];
111125
}) {
112-
const setRef = useSetRef(props.meta);
126+
const setRef = useContextRef(props.type || "", props.meta);
113127
return props.children(setRef);
114128
}

0 commit comments

Comments
 (0)
Please sign in to comment.