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

问题描述

我有 App.tsx,其中包含 2 个兄弟组件:

  • Konva.tsx:它有画布
  • Options.tsx:它有一个下载画布按钮

因此我在 ref 中创建了一个名为 stageRefApp.tsx 以将其传递给 Konva.tsxOptions.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 })
  }
}

类型/index.ts

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

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

TS2531:对象可能为“空”。

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 但我不知道如何解决这个问题?

解决方法

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

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

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

App.tsx

Konva

Konva.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

Options.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>
      )
    }
  )
)

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