兄弟组件之间的 React Konva 共享引用抛出错误

React Konva share ref between sibling components throws error

我有 App.tsx,其中包含 2 个同级组件:

所以我在 App.tsx 中创建了一个名为 stageRefref 以将其传递给 Konva.tsx & Options.tsx。我使用 React.forwardRef 将引用转发给子组件。

App.tsx

import * as React from 'react'
import type { Stage as StageType } from 'konva/types/Stage'

import { Konva, Options } from '@/components/index'
import { FrameItProvider } from '@/store/index'

const App = () => {
  const stageRef = React.createRef<StageType>()

  return (
    <>
      <Konva ref={stageRef} />
      <Options ref={stageRef} />
    </>
  )
}

export default App

Konva.tsx中,ref指向Canvas,因此它可以访问DOM中的元素。

Konva.tsx

import * as React from 'react'
import { observer } from 'mobx-react'

import { useFrameItStore } from '@/store/index'
import { BrowserWindow } from '@/components/index'

import type { Window } from '@/types/index'
import type { Stage as StageType } from 'konva/types/Stage'

interface IProps {
  className?: string
}

export const Konva = observer(
  React.forwardRef<StageType, IProps>(({ className }: IProps, forwardedRef) => {
    const frameItStore = useFrameItStore()
    const browser: Window = frameItStore.browser

    return (
      <>
        <Stage
          width={browser.width}
          height={browser.height}
          ref={forwardedRef}
          className={className}
        >
          <Layer>
            <BrowserWindow />
          </Layer>
        </Stage>
      </>
    )
  })
)

Options.tsx 中,我使用 downloadImageforwardedRef 触发下载调用。

Options.tsx

import * as React from 'react'
import { observer } from 'mobx-react'
import type { Stage as StageType } from 'konva/types/Stage'

import { useFrameItStore } from '@/store/index'
import type { TrafficSignalStyle } from '@/types/index'

interface IProps {
  className?: string
}

export const Options = observer(
  React.forwardRef<StageType, IProps>((props: IProps, forwardedRef) => {
    const frameItStore = useFrameItStore()

    const downloadImage: (stageRef: React.ForwardedRef<StageType>) => void =
      frameItStore.downloadImage

    return (
      <div>
        <button onClick={() => downloadImage(forwardedRef)}>
          Download Canvas
        </button>
      </div>
    )
  })
)

我正在使用 MobX 来管理我的商店。但是,forwardRef 会导致问题。

store/index.ts

import type { Stage as StageType } from 'konva/types/Stage'

import type { IFrameItStore } from '@/types/index'

export class FrameItStore implements IFrameItStore {
  downloadImage(stageRef: React.ForwardedRef<StageType>) {
    console.log(stageRef)
    stageRef
      .current!.getStage()
      .toDataURL({ mimeType: 'image/jpeg', quality: 1 })
  }
}

types/index.ts

export interface IFrameItStore {
    downloadImage(stageRef: React.ForwardedRef<StageType>): void
}

我在 store/index.ts 中遇到 2 个 TypeScript 错误:

TS2531: Object is possibly 'null'.

stageRef 当我尝试访问 stageRef.current

  TS2339: Property 'current' does not exist on type '((instance: Stage | null) => void) | MutableRefObject<Stage | null>'.

属性 'current' 不存在于类型 '(instance: Stage | null) => void' 上。

current

我试过不使用 ForwardedRef 但它给出了类型不匹配的错误所以我必须使用 ForwardedRef 但我不确定如何解决这个问题?

我在 App.tsx 中创建了一个 ref,然后将其传递给了 Konva.tsx。我使用 React.forwardRef 函数捕获了 ref。

然后我使用 Konva.tsx 中鲜为人知的 useImperativeHandle 钩子来访问 App.tsx 中的函数 downloadImage 然后将函数 downloadImage 直接传递给Options.tsx.

我没有在 MobX 商店中保留任何东西。只是将它保存在本地的 Konva 组件中,以便于访问。旧规则是,如果它不打算在任何其他组件中使用,那么它应该尽可能靠近组件。

App.tsx

import * as React from 'react'

import { Konva, Options } from '@/components/index'

const App = () => {
  const stageRef = React.useRef<{ downloadImage: Function }>(null)

  return (
    <>
      <Konva ref={stageRef} />
      <Options downloadImage={() => stageRef?.current?.downloadImage()} />
    </>
  )
}

export default App

Konva.tsx

import * as React from 'react'
import { observer } from 'mobx-react'

import { useFrameItStore } from '@/store/index'
import { BrowserWindow } from '@/components/index'

import type { Window } from '@/types/index'
import type { Stage as StageType } from 'konva/types/Stage'

interface IProps {
  className?: string
}

interface ForwardedRef {
  downloadImage: Function
}

export const Konva = observer(
  React.forwardRef<ForwardedRef, IProps>(
    ({ className }: IProps, forwardedRef) => {
      const frameItStore = useFrameItStore()
      const browser: Window = frameItStore.browser

      const stageRef = React.useRef<StageType>(null)

      React.useImperativeHandle(
        forwardedRef,
        () => ({
          downloadImage: () =>
            stageRef.current
              ?.getStage()
              .toDataURL({ mimeType: 'image/jpeg', quality: 1 }),
        }),
        []
      )

      return (
        <Stage
          width={browser.width}
          height={browser.height}
          ref={stageRef}
          className={className}
        >
          <Layer>
            <BrowserWindow />
          </Layer>
        </Stage>
      )
    }
  )
)

Options.tsx

import * as React from 'react'
import { observer } from 'mobx-react'

interface IProps {
  className?: string
  downloadImage: Function
}

export const Options = observer((props: IProps) => {
  const { downloadImage } = props

  return (
    <div>
      <button onClick={() => console.log(downloadImage())}>
        Download Image
      </button>
    </div>
  )
})

这是 CodeSandBox 上的完整工作演示 → https://codesandbox.io/s/react-konva-share-refs-between-siblings-dsd97?file=/src/App.tsx