Skip to content

Commit 83f18cb

Browse files
committed
wip: simple single field use complete
1 parent 5ec0fa0 commit 83f18cb

File tree

9 files changed

+186
-132
lines changed

9 files changed

+186
-132
lines changed

fast-context-generic-extended/src/App.tsx

Lines changed: 15 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,24 @@
1-
import createFastContext from "./createFastContext";
2-
3-
const { Provider, useStore } = createFastContext({
4-
first: "",
5-
last: "",
1+
import createFastContext from "./app/createFastContext";
2+
import MainPage from "./pages/MainPage";
3+
4+
export const {
5+
FastContextProvider:AppFastContextProvider,
6+
useFastContextField:useAppFastContextField,
7+
useFastContextFields:useAppFastContextFields
8+
} = createFastContext({
9+
first: "" as string,
10+
last: "" as string,
611
});
712

8-
const TextInput = ({ value }: { value: "first" | "last" }) => {
9-
const [fieldValue, setStore] = useStore((store) => store[value]);
10-
return (
11-
<div className="field">
12-
{value}:{" "}
13-
<input
14-
value={fieldValue}
15-
onChange={(e) => setStore({ [value]: e.target.value })}
16-
/>
17-
</div>
18-
);
19-
};
20-
21-
const Display = ({ value }: { value: "first" | "last" }) => {
22-
const [fieldValue] = useStore((store) => store[value]);
23-
return (
24-
<div className="value">
25-
{value}: {fieldValue}
26-
</div>
27-
);
28-
};
29-
30-
const FormContainer = () => {
31-
return (
32-
<div className="container">
33-
<h5>FormContainer</h5>
34-
<TextInput value="first" />
35-
<TextInput value="last" />
36-
</div>
37-
);
38-
};
39-
40-
const DisplayContainer = () => {
41-
return (
42-
<div className="container">
43-
<h5>DisplayContainer</h5>
44-
<Display value="first" />
45-
<Display value="last" />
46-
</div>
47-
);
48-
};
49-
50-
const ContentContainer = () => {
51-
return (
52-
<div className="container">
53-
<h5>ContentContainer</h5>
54-
<FormContainer />
55-
<DisplayContainer />
56-
</div>
57-
);
58-
};
59-
6013
function App() {
14+
console.log(`App Rendering`)
6115
return (
62-
<Provider>
16+
<AppFastContextProvider>
6317
<div className="container">
64-
<h5>App</h5>
65-
<ContentContainer />
18+
<h1>App</h1>
19+
<MainPage />
6620
</div>
67-
</Provider>
21+
</AppFastContextProvider>
6822
);
6923
}
7024

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import React, {
2+
useRef,
3+
createContext,
4+
useContext,
5+
useCallback,
6+
useSyncExternalStore,
7+
} from "react";
8+
9+
export default function createFastContext<FastContext>(initialState: FastContext) {
10+
function useFastContextData(): {
11+
get: () => FastContext;
12+
set: (value: Partial<FastContext>) => void;
13+
subscribe: (callback: () => void) => () => void;
14+
} {
15+
const store = useRef(initialState);
16+
17+
const get = useCallback(() => store.current, []);
18+
19+
const subscribers = useRef(new Set<() => void>());
20+
21+
const set = useCallback((value: Partial<FastContext>) => {
22+
console.log('set', value);
23+
store.current = { ...store.current, ...value };
24+
subscribers.current.forEach((callback) => callback());
25+
}, []);
26+
27+
const subscribe = useCallback((callback: () => void) => {
28+
subscribers.current.add(callback);
29+
return () => subscribers.current.delete(callback);
30+
}, []);
31+
32+
return {
33+
get,
34+
set,
35+
subscribe,
36+
};
37+
}
38+
39+
type UseFastContextDataReturnType = ReturnType<typeof useFastContextData>;
40+
41+
const FastContext = createContext<UseFastContextDataReturnType | null>(null);
42+
43+
function FastContextProvider({ children }: Readonly<{ children: React.ReactNode }>) {
44+
return (
45+
<FastContext.Provider value={useFastContextData()}>
46+
{children}
47+
</FastContext.Provider>
48+
);
49+
}
50+
51+
function useFastContext<SelectorOutput>(
52+
selector: (store: FastContext) => SelectorOutput
53+
): [SelectorOutput, (value: Partial<FastContext>) => void] {
54+
const fastContext = useContext(FastContext);
55+
if (!fastContext) {
56+
throw new Error("Store not found");
57+
}
58+
59+
const state = useSyncExternalStore(
60+
fastContext.subscribe,
61+
() => selector(fastContext.get()),
62+
() => selector(initialState),
63+
);
64+
65+
return [state, fastContext.set];
66+
}
67+
68+
function useFastContextField<SelectorOutput>(
69+
field: string
70+
): [SelectorOutput, (value: SelectorOutput) => void]{
71+
const [fieldValue, setter] = useFastContext((store) => (store as Record<string, SelectorOutput>)[field]);
72+
const setField = (value: any) => setter({ [field]: value } as Partial<FastContext>);
73+
return [fieldValue, setField as (value: SelectorOutput) => void];
74+
}
75+
76+
function useFastContextFields<SelectorOutput>(
77+
field: string[]
78+
): [SelectorOutput, (value: Partial<FastContext>) => void][] {
79+
return [useFastContext((store) => (store as Record<string, SelectorOutput>)[field[0]])];
80+
}
81+
82+
return {
83+
FastContextProvider,
84+
useFastContextField,
85+
useFastContextFields,
86+
};
87+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import FormContainer from "./FormContainer";
2+
import DisplayContainer from "./DisplayContainer";
3+
4+
export default function ContentContainer() {
5+
console.log(`Content Rendering`)
6+
return (
7+
<div className="container">
8+
<h3>ContentContainer</h3>
9+
<FormContainer />
10+
<DisplayContainer />
11+
</div>
12+
);
13+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
type Props = {
2+
label?: string;
3+
value: string;
4+
};
5+
export default function Display({label, value}: Readonly<Props>) {
6+
console.log(`${label} display change`)
7+
return (
8+
<div className="value">
9+
{label ? <label>{label} : </label> : null}
10+
<input value={value} readOnly style={{backgroundColor: "#eee", cursor: 'auto', border: 0}}/>
11+
</div>
12+
);
13+
};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { useAppFastContextField } from "../App";
2+
import Display from "./Display";
3+
4+
export default function DisplayContainer() {
5+
console.log(`Display Rendering`)
6+
const [first] = useAppFastContextField('first');
7+
const [last] = useAppFastContextField('last');
8+
return (
9+
<div className="container">
10+
<h4>DisplayContainer</h4>
11+
<Display label="First Name" value={first as string} />
12+
<Display label="Last Name" value={last as string} />
13+
</div>
14+
);
15+
};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { useAppFastContextField } from "../App";
2+
import TextInput from "./TextInput";
3+
4+
export default function FormContainer() {
5+
console.log(`Form Rendering`)
6+
const [firstName, setFirstName] = useAppFastContextField("first");
7+
const [lastName, setLastName] = useAppFastContextField("last");
8+
return (
9+
<div className="container">
10+
<h4>FormContainer</h4>
11+
<TextInput value={firstName as string} label='First Name' onChange={setFirstName}/>
12+
<TextInput value={lastName as string} label='Last Name' onChange={setLastName} />
13+
</div>
14+
);
15+
};
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
type Props = {
2+
label?: string;
3+
value: string;
4+
onChange: (value: string) => void;
5+
};
6+
export default function TextInput( { label = '', value, onChange}: Readonly<Props> ) {
7+
return (
8+
<div className="field">
9+
{label ? <label>{label} : </label> : null}
10+
<input
11+
value={value}
12+
onChange={(e) => onChange(e.target.value)}
13+
/>
14+
</div>
15+
);
16+
};

fast-context-generic-extended/src/createFastContext.tsx

Lines changed: 0 additions & 71 deletions
This file was deleted.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import ContentContainer from "../components/ContentContainer";
2+
3+
4+
export default function MainPage() {
5+
console.log(`Page Rendering`)
6+
return (
7+
<div className="container">
8+
<h2>Page</h2>
9+
<ContentContainer />
10+
</div>
11+
);
12+
}

0 commit comments

Comments
 (0)