【问题标题】:React Konva share ref between sibling components throws errorReact Konva 在兄弟组件之间共享 ref 引发错误
【发布时间】:2020-12-28 10:38:44
【问题描述】:

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

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

所以我在App.tsx 中创建了一个名为stageRefref,将其传递给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 会导致问题。

存储/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:对象可能为“空”。

当我尝试访问stageRef.current 时,在stageRef

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

类型“(实例:Stage | null)=> void”上不存在属性“当前”。

current

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

【问题讨论】:

    标签: reactjs typescript konvajs react-ref react-konva


    【解决方案1】:

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

    然后我在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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-01-04
      • 2018-02-17
      • 2017-10-01
      • 1970-01-01
      • 2017-10-11
      • 2018-03-10
      • 2018-04-05
      相关资源
      最近更新 更多