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)
|
// @internal (undocumented)
|
||||||
export const EditorContext: React_2.Context<Editor>;
|
export const EditorContext: React_2.Context<Editor | null>;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export class Ellipse2d extends Geometry2d {
|
export class Ellipse2d extends Geometry2d {
|
||||||
|
|
|
@ -2,9 +2,15 @@ import React from 'react'
|
||||||
import { Editor } from '../editor/Editor'
|
import { Editor } from '../editor/Editor'
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export const EditorContext = React.createContext({} as Editor)
|
export const EditorContext = React.createContext<Editor | null>(null)
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export function useEditor(): Editor {
|
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
|
} & ErrorComponents
|
||||||
>
|
>
|
||||||
|
|
||||||
const EditorComponentsContext = createContext({} as TLEditorComponents & ErrorComponents)
|
const EditorComponentsContext = createContext<null | (TLEditorComponents & ErrorComponents)>(null)
|
||||||
|
|
||||||
type ComponentsContextProviderProps = {
|
type ComponentsContextProviderProps = {
|
||||||
overrides?: TLEditorComponents
|
overrides?: TLEditorComponents
|
||||||
|
@ -140,5 +140,9 @@ export function EditorComponentsProvider({
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export function useEditorComponents() {
|
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;
|
export function useCopyAs(): (ids: TLShapeId[], format?: TLCopyType) => void;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export const useCurrentTranslation: () => TLUiTranslation;
|
export function useCurrentTranslation(): TLUiTranslation;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export function useDefaultColorTheme(): {
|
export function useDefaultColorTheme(): {
|
||||||
|
|
|
@ -16,11 +16,15 @@ export type TldrawUiMenuContextType =
|
||||||
const menuContext = createContext<{
|
const menuContext = createContext<{
|
||||||
type: TldrawUiMenuContextType
|
type: TldrawUiMenuContextType
|
||||||
sourceId: TLUiEventSource
|
sourceId: TLUiEventSource
|
||||||
}>({ type: 'menu', sourceId: 'main-menu' })
|
} | null>(null)
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export function useTldrawUiMenuContext() {
|
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 */
|
/** @public */
|
||||||
|
|
|
@ -56,7 +56,7 @@ export interface TLUiActionItem<
|
||||||
export type TLUiActionsContextType = Record<string, TLUiActionItem>
|
export type TLUiActionsContextType = Record<string, TLUiActionItem>
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export const ActionsContext = React.createContext<TLUiActionsContextType>({})
|
export const ActionsContext = React.createContext<TLUiActionsContextType | null>(null)
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export type ActionsProviderProps = {
|
export type ActionsProviderProps = {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { useEditor, useValue } from '@tldraw/editor'
|
||||||
import React, { ReactNode, useContext } from 'react'
|
import React, { ReactNode, useContext } from 'react'
|
||||||
import { PORTRAIT_BREAKPOINT, PORTRAIT_BREAKPOINTS } from '../constants'
|
import { PORTRAIT_BREAKPOINT, PORTRAIT_BREAKPOINTS } from '../constants'
|
||||||
|
|
||||||
const BreakpointContext = React.createContext(0)
|
const BreakpointContext = React.createContext<number | null>(null)
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export function BreakPointProvider({
|
export function BreakPointProvider({
|
||||||
|
@ -40,5 +40,9 @@ export function BreakPointProvider({
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export function useBreakpoint() {
|
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
|
[K in keyof BaseTLUiComponents]: BaseTLUiComponents[K] | null
|
||||||
}>
|
}>
|
||||||
|
|
||||||
const TldrawUiComponentsContext = createContext({} as TLUiComponents)
|
const TldrawUiComponentsContext = createContext<TLUiComponents | null>(null)
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export type TLUiComponentsProviderProps = {
|
export type TLUiComponentsProviderProps = {
|
||||||
|
@ -105,5 +105,9 @@ export function TldrawUiComponentsProvider({
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export function useTldrawUiComponents() {
|
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 */
|
/** @internal */
|
||||||
export const DialogsContext = createContext({} as TLUiDialogsContextType)
|
export const DialogsContext = createContext<TLUiDialogsContextType | null>(null)
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export type DialogsProviderProps = {
|
export type DialogsProviderProps = {
|
||||||
|
|
|
@ -115,7 +115,7 @@ const defaultEventHandler: TLUiEventHandler = () => void null
|
||||||
export type TLUiEventContextType = TLUiEventHandler<keyof TLUiEventMap>
|
export type TLUiEventContextType = TLUiEventHandler<keyof TLUiEventMap>
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export const EventsContext = React.createContext<TLUiEventContextType>({} as TLUiEventContextType)
|
export const EventsContext = React.createContext<TLUiEventContextType | null>(null)
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export type EventsProviderProps = {
|
export type EventsProviderProps = {
|
||||||
|
|
|
@ -33,7 +33,7 @@ export type TLUiToastsContextType = {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export const ToastsContext = createContext({} as TLUiToastsContextType)
|
export const ToastsContext = createContext<TLUiToastsContextType | null>(null)
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export type ToastsProviderProps = {
|
export type ToastsProviderProps = {
|
||||||
|
|
|
@ -28,7 +28,7 @@ export interface TLUiToolItem<
|
||||||
export type TLUiToolsContextType = Record<string, TLUiToolItem>
|
export type TLUiToolsContextType = Record<string, TLUiToolItem>
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export const ToolsContext = React.createContext({} as TLUiToolsContextType)
|
export const ToolsContext = React.createContext<null | TLUiToolsContextType>(null)
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export type TLUiToolsProviderProps = {
|
export type TLUiToolsProviderProps = {
|
||||||
|
|
|
@ -23,12 +23,16 @@ export interface TLUiTranslationProviderProps {
|
||||||
/** @public */
|
/** @public */
|
||||||
export type TLUiTranslationContextType = TLUiTranslation
|
export type TLUiTranslationContextType = TLUiTranslation
|
||||||
|
|
||||||
const TranslationsContext = React.createContext<TLUiTranslationContextType>(
|
const TranslationsContext = React.createContext<TLUiTranslationContextType | null>(null)
|
||||||
{} as TLUiTranslationContextType
|
|
||||||
)
|
|
||||||
|
|
||||||
/** @public */
|
/** @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.
|
* Provides a translation context to the editor.
|
||||||
|
|
Loading…
Reference in a new issue