Skip to content

Instantly share code, notes, and snippets.

@JLarky

JLarky/context.tsx

Created Aug 25, 2020
Embed
What would you like to do?
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>
)}
</>
);
};
@abhisekpadhi

This comment has been minimized.

Copy link

@abhisekpadhi abhisekpadhi commented Feb 11, 2021

Neat 👍

@kutsan

This comment has been minimized.

Copy link

@kutsan kutsan commented Feb 13, 2021

Thanks! ReturnType to the rescue!

@TafkaMax

This comment has been minimized.

Copy link

@TafkaMax TafkaMax commented Mar 3, 2021

what if i dont have a return type for the useprovidervalue? I just have useState set in there and nothing else.

EDIT: Should i return the useState variable. so const[variable, setVariable] -> in the end i return the variable, so my the context gets the return type of the state.

@JLarky

This comment has been minimized.

Copy link
Owner Author

@JLarky JLarky commented Mar 4, 2021

what if i dont have a return type for the useprovidervalue? I just have useState set in there and nothing else.

@TafkaMax, I would do something like this:

function useProviderValue() {
  const [moved, setMoved] = React.useState(false);
  return [moved, setMoved] as const;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment