Skip to content

Instantly share code, notes, and snippets.

@pzi
Forked from JLarky/README.md
Created April 12, 2022 07:27
Show Gist options
  • Save pzi/fea159dd12c0573fd8b35034a4da8a7f to your computer and use it in GitHub Desktop.
Save pzi/fea159dd12c0573fd8b35034a4da8a7f to your computer and use it in GitHub Desktop.
Ultimate example of react context hook with nice type-safe (TypeScript) wrappers and reduced boilerplate by using `ReturnType`
import React from "react";
import { useImmer } from "use-immer";
function useProviderValue() {
const [moved, setMoved] = React.useState(false);
const [point, setPoint] = useImmer<{
x: number;
y: number;
}>({ x: 0, y: 0 }); // using immer to illustrate that you can easily derive setPoint type instead of writing types for Context manually
const value = React.useMemo(
() => ({
moved,
setMoved,
point,
setPoint,
}),
[moved, point, setPoint]
);
return value;
}
export type Context = ReturnType<typeof useProviderValue>;
const Context = React.createContext<Context | undefined>(undefined);
Context.displayName = "Context";
export const Provider: React.FC = props => {
const value = useProviderValue();
return <Context.Provider value={value} {...props} />;
};
export function useContext() {
const context = React.useContext(Context);
if (context === undefined) {
throw new Error(`useContext must be used within a Provider`);
}
return context;
}
export function useMoved() {
const { moved } = useContext();
return moved;
}
export function useListenMouseMove() {
const { setMoved, setPoint } = useContext();
const isMounted = React.useRef(false);
React.useEffect(() => {
isMounted.current = true;
const listen = (e: MouseEvent) => {
if (isMounted.current) {
setPoint(draft => {
draft.x = e.x;
draft.y = e.y;
});
setMoved(true);
}
};
document.addEventListener("mousemove", listen);
return () => {
isMounted.current = false;
document.removeEventListener("mousemove", listen);
};
}, [setMoved, setPoint]);
return;
}
export const Example = () => {
return (
<Provider>
<Test1 />
<Test2 />
</Provider>
);
};
const Test1 = () => {
useListenMouseMove();
return null;
};
const Test2 = () => {
const { point } = useContext();
const hasMoved = useMoved();
return (
<>
{hasMoved && (
<span>
({point.x},{point.y})
</span>
)}
</>
);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment