No defaults for contexts (#3750)
in many places, we use a pattern like `React.createContext({} as Editor)` when defining contexts. This causes a problem: `{}` is not `Editor`, but you can still `useEditor` wherever you like and your code with run with this confusing non-editor value. This diff updates all our `createContext` calls to default to `null`, with an explicit check and error for missing values. Now, if you `useEditor` outside of `<Tldraw />`, you'll get a message telling you that it can only be used within `<Tldraw />`. ### Change Type - [x] `sdk` — Changes the tldraw SDK - [x] `improvement` — Improving existing features ### Release Notes `useEditor` and other context-based hooks will now throw an error when used out-of-context, instead of returning a fake value.
This commit is contained in:
parent
ab807afda3
commit
5b21ad96ae
13 changed files with 47 additions and 21 deletions
|
@ -1068,7 +1068,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
}
|
||||
|
||||
// @internal (undocumented)
|
||||
export const EditorContext: React_2.Context<Editor>;
|
||||
export const EditorContext: React_2.Context<Editor | null>;
|
||||
|
||||
// @public (undocumented)
|
||||
export class Ellipse2d extends Geometry2d {
|
||||
|
|
|
@ -2,9 +2,15 @@ import React from 'react'
|
|||
import { Editor } from '../editor/Editor'
|
||||
|
||||
/** @internal */
|
||||
export const EditorContext = React.createContext({} as Editor)
|
||||
export const EditorContext = React.createContext<Editor | null>(null)
|
||||
|
||||
/** @public */
|
||||
export function useEditor(): Editor {
|
||||
return React.useContext(EditorContext)
|
||||
const editor = React.useContext(EditorContext)
|
||||
if (!editor) {
|
||||
throw new Error(
|
||||
'useEditor must be used inside of the <Tldraw /> or <TldrawEditor /> components'
|
||||
)
|
||||
}
|
||||
return editor
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ export type TLEditorComponents = Partial<
|
|||
} & ErrorComponents
|
||||
>
|
||||
|
||||
const EditorComponentsContext = createContext({} as TLEditorComponents & ErrorComponents)
|
||||
const EditorComponentsContext = createContext<null | (TLEditorComponents & ErrorComponents)>(null)
|
||||
|
||||
type ComponentsContextProviderProps = {
|
||||
overrides?: TLEditorComponents
|
||||
|
@ -140,5 +140,9 @@ export function EditorComponentsProvider({
|
|||
|
||||
/** @public */
|
||||
export function useEditorComponents() {
|
||||
return useContext(EditorComponentsContext)
|
||||
const components = useContext(EditorComponentsContext)
|
||||
if (!components) {
|
||||
throw new Error('useEditorComponents must be used inside of <EditorComponentsProvider />')
|
||||
}
|
||||
return components
|
||||
}
|
||||
|
|
|
@ -2566,7 +2566,7 @@ export function useCanUndo(): boolean;
|
|||
export function useCopyAs(): (ids: TLShapeId[], format?: TLCopyType) => void;
|
||||
|
||||
// @public (undocumented)
|
||||
export const useCurrentTranslation: () => TLUiTranslation;
|
||||
export function useCurrentTranslation(): TLUiTranslation;
|
||||
|
||||
// @public (undocumented)
|
||||
export function useDefaultColorTheme(): {
|
||||
|
|
|
@ -16,11 +16,15 @@ export type TldrawUiMenuContextType =
|
|||
const menuContext = createContext<{
|
||||
type: TldrawUiMenuContextType
|
||||
sourceId: TLUiEventSource
|
||||
}>({ type: 'menu', sourceId: 'main-menu' })
|
||||
} | null>(null)
|
||||
|
||||
/** @public */
|
||||
export function useTldrawUiMenuContext() {
|
||||
return useContext(menuContext)
|
||||
const context = useContext(menuContext)
|
||||
if (!context) {
|
||||
throw new Error('useTldrawUiMenuContext must be used within a TldrawUiMenuContextProvider')
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
/** @public */
|
||||
|
|
|
@ -56,7 +56,7 @@ export interface TLUiActionItem<
|
|||
export type TLUiActionsContextType = Record<string, TLUiActionItem>
|
||||
|
||||
/** @internal */
|
||||
export const ActionsContext = React.createContext<TLUiActionsContextType>({})
|
||||
export const ActionsContext = React.createContext<TLUiActionsContextType | null>(null)
|
||||
|
||||
/** @public */
|
||||
export type ActionsProviderProps = {
|
||||
|
|
|
@ -2,7 +2,7 @@ import { useEditor, useValue } from '@tldraw/editor'
|
|||
import React, { ReactNode, useContext } from 'react'
|
||||
import { PORTRAIT_BREAKPOINT, PORTRAIT_BREAKPOINTS } from '../constants'
|
||||
|
||||
const BreakpointContext = React.createContext(0)
|
||||
const BreakpointContext = React.createContext<number | null>(null)
|
||||
|
||||
/** @public */
|
||||
export function BreakPointProvider({
|
||||
|
@ -40,5 +40,9 @@ export function BreakPointProvider({
|
|||
|
||||
/** @public */
|
||||
export function useBreakpoint() {
|
||||
return useContext(BreakpointContext)
|
||||
const breakpoint = useContext(BreakpointContext)
|
||||
if (breakpoint === null) {
|
||||
throw new Error('useBreakpoint must be used inside of the <BreakpointProvider /> component')
|
||||
}
|
||||
return breakpoint
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ export type TLUiComponents = Partial<{
|
|||
[K in keyof BaseTLUiComponents]: BaseTLUiComponents[K] | null
|
||||
}>
|
||||
|
||||
const TldrawUiComponentsContext = createContext({} as TLUiComponents)
|
||||
const TldrawUiComponentsContext = createContext<TLUiComponents | null>(null)
|
||||
|
||||
/** @public */
|
||||
export type TLUiComponentsProviderProps = {
|
||||
|
@ -105,5 +105,9 @@ export function TldrawUiComponentsProvider({
|
|||
|
||||
/** @public */
|
||||
export function useTldrawUiComponents() {
|
||||
return useContext(TldrawUiComponentsContext)
|
||||
const components = useContext(TldrawUiComponentsContext)
|
||||
if (!components) {
|
||||
throw new Error('useTldrawUiComponents must be used within a TldrawUiComponentsProvider')
|
||||
}
|
||||
return components
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ export type TLUiDialogsContextType = {
|
|||
}
|
||||
|
||||
/** @internal */
|
||||
export const DialogsContext = createContext({} as TLUiDialogsContextType)
|
||||
export const DialogsContext = createContext<TLUiDialogsContextType | null>(null)
|
||||
|
||||
/** @internal */
|
||||
export type DialogsProviderProps = {
|
||||
|
|
|
@ -115,7 +115,7 @@ const defaultEventHandler: TLUiEventHandler = () => void null
|
|||
export type TLUiEventContextType = TLUiEventHandler<keyof TLUiEventMap>
|
||||
|
||||
/** @internal */
|
||||
export const EventsContext = React.createContext<TLUiEventContextType>({} as TLUiEventContextType)
|
||||
export const EventsContext = React.createContext<TLUiEventContextType | null>(null)
|
||||
|
||||
/** @public */
|
||||
export type EventsProviderProps = {
|
||||
|
|
|
@ -33,7 +33,7 @@ export type TLUiToastsContextType = {
|
|||
}
|
||||
|
||||
/** @internal */
|
||||
export const ToastsContext = createContext({} as TLUiToastsContextType)
|
||||
export const ToastsContext = createContext<TLUiToastsContextType | null>(null)
|
||||
|
||||
/** @internal */
|
||||
export type ToastsProviderProps = {
|
||||
|
|
|
@ -28,7 +28,7 @@ export interface TLUiToolItem<
|
|||
export type TLUiToolsContextType = Record<string, TLUiToolItem>
|
||||
|
||||
/** @internal */
|
||||
export const ToolsContext = React.createContext({} as TLUiToolsContextType)
|
||||
export const ToolsContext = React.createContext<null | TLUiToolsContextType>(null)
|
||||
|
||||
/** @public */
|
||||
export type TLUiToolsProviderProps = {
|
||||
|
|
|
@ -23,12 +23,16 @@ export interface TLUiTranslationProviderProps {
|
|||
/** @public */
|
||||
export type TLUiTranslationContextType = TLUiTranslation
|
||||
|
||||
const TranslationsContext = React.createContext<TLUiTranslationContextType>(
|
||||
{} as TLUiTranslationContextType
|
||||
)
|
||||
const TranslationsContext = React.createContext<TLUiTranslationContextType | null>(null)
|
||||
|
||||
/** @public */
|
||||
export const useCurrentTranslation = () => React.useContext(TranslationsContext)
|
||||
export function useCurrentTranslation() {
|
||||
const translations = React.useContext(TranslationsContext)
|
||||
if (!translations) {
|
||||
throw new Error('useCurrentTranslation must be used inside of <TldrawUiContextProvider />')
|
||||
}
|
||||
return translations
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a translation context to the editor.
|
||||
|
|
Loading…
Reference in a new issue