By using ReturnType
we don't have to manually write type for Context
See also gist for SolidJS https://gist.github.com/JLarky/a46055f673a2cb021db1a34449e3be07
And original tweet https://twitter.com/JLarky/status/1554152932425117697
By using ReturnType
we don't have to manually write type for Context
See also gist for SolidJS https://gist.github.com/JLarky/a46055f673a2cb021db1a34449e3be07
And original tweet https://twitter.com/JLarky/status/1554152932425117697
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 = (props: React.PropsWithChildren) => { | |
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> | |
)} | |
</> | |
); | |
}; |
// minimal example | |
import type { PropsWithChildren } from "react"; | |
import { createContext, useContext, useMemo, useState } from "react"; | |
function useProviderValue() { | |
const [isDark, setIsDark] = useState(false); | |
return useMemo(() => ({ isDark, setIsDark }), []); | |
} | |
export type Context = ReturnType<typeof useProviderValue>; | |
const DarkContext = createContext<Context | undefined>(undefined); | |
DarkContext.displayName = "DarkProvider"; | |
export const DarkProvider = (props: PropsWithChildren) => { | |
const value = useProviderValue(); | |
return <DarkContext.Provider value={value} {...props} />; | |
}; | |
export function useDark() { | |
const context = useContext(DarkContext); | |
if (context === undefined) { | |
throw new Error(`useDark must be used within a DarkProvider`); | |
} | |
return context; | |
} | |
export function useIsDark() { | |
return useDark().isDark; | |
} | |
export function useSetDark() { | |
return useDark().setIsDark; | |
} |
import type { PropsWithChildren } from "react"; | |
import { createContext, useContext, useMemo, useState } from "react"; | |
function useProviderValue() { | |
const [isDark, setIsDark] = useState(false); | |
const value = useMemo( | |
() => ({ | |
isDark, | |
setIsDark, | |
}), | |
[] | |
); | |
return value; | |
} | |
export type Context = ReturnType<typeof useProviderValue>; | |
const DarkContext = createContext<Context>({ | |
isDark: false, | |
setIsDark: () => {}, | |
}); | |
DarkContext.displayName = "DarkProvider"; | |
export const DarkProvider = (props: PropsWithChildren) => { | |
const value = useProviderValue(); | |
return <DarkContext.Provider value={value} {...props} />; | |
}; | |
export function useDark() { | |
return useContext(DarkContext); | |
} | |
export function useIsDark() { | |
return useDark().isDark; | |
} | |
export function useSetDark() { | |
return useDark().setIsDark; | |
} |