Skip to content

React Native Issues #72

Description

@matthewcarlreetz

When building for React Native I run into quite a few issues, some I have been able to resolve, but now I'm stuck. I looked through the closed issues and it seems React Native is supported. Is there a guide or example somewhere?

Here's the steps I've taken...

First I needed to add react-native-canvas and react-native-fs to the project.

Then I polyfilled window and navigator

global.window = {
  navigator: {
    userAgent: 'any',
    platform: 'any',
  },
  location: { search: 'location' },
}
global.navigator = {
  userAgent: 'any',
  platform: 'any',
}

I modified the code to use Canvas

/* eslint-disable no-inner-declarations */
import React, { useEffect, useRef, useState, forwardRef } from 'react'
import { Application } from '@splinetool/runtime'
import type {
  SPEObject,
  SplineEvent,
  SplineEventName,
} from '@splinetool/runtime'
import Canvas from 'react-native-canvas'
import { StyleProp, ViewStyle } from 'react-native'

export type { SPEObject, SplineEvent, SplineEventName }

export function mergeRefs<T = any>(
  refs: Array<React.MutableRefObject<T> | React.LegacyRef<T>>,
): React.RefCallback<T> {
  return (value) => {
    refs.forEach((ref) => {
      if (typeof ref === 'function') {
        ref(value)
      } else if (ref != null) {
        const typedRef = ref as React.MutableRefObject<T | null>
        typedRef.current = value
      }
    })
  }
}

type CanvasAttributes = React.CanvasHTMLAttributes<HTMLCanvasElement>
export interface SplineProps
  extends Omit<
    CanvasAttributes,
    | 'onLoad'
    | 'onMouseDown'
    | 'onMouseUp'
    | 'onMouseHover'
    | 'onKeyDown'
    | 'onKeyUp'
    | 'onWheel'
    | 'style'
  > {
  scene: string
  onLoad?: (e: Application) => void
  onMouseDown?: (e: SplineEvent) => void
  onMouseUp?: (e: SplineEvent) => void
  onMouseHover?: (e: SplineEvent) => void
  onKeyDown?: (e: SplineEvent) => void
  onKeyUp?: (e: SplineEvent) => void
  onStart?: (e: SplineEvent) => void
  onLookAt?: (e: SplineEvent) => void
  onFollow?: (e: SplineEvent) => void
  onWheel?: (e: SplineEvent) => void
  autoRender?: boolean
  style?: StyleProp<ViewStyle>
}

const Spline = forwardRef<HTMLCanvasElement, SplineProps>(
  (
    {
      scene,
      style,
      onMouseDown,
      onMouseUp,
      onMouseHover,
      onKeyDown,
      onKeyUp,
      onStart,
      onLookAt,
      onFollow,
      onWheel,
      onLoad,
      autoRender = false,
      ...props
    },
    ref,
  ) => {
    const canvasRef = useRef<HTMLCanvasElement>(null)
    const [, setIsLoading] = useState(true)

    // Initialize runtime when component is mounted
    useEffect(() => {
      setIsLoading(true)

      let speApp: Application
      const events: {
        name: SplineEventName
        cb?: (e: SplineEvent) => void
      }[] = [
        {
          name: 'mouseDown',
          cb: onMouseDown,
        },
        {
          name: 'mouseUp',
          cb: onMouseUp,
        },
        {
          name: 'mouseHover',
          cb: onMouseHover,
        },
        {
          name: 'keyDown',
          cb: onKeyDown,
        },
        {
          name: 'keyUp',
          cb: onKeyUp,
        },
        {
          name: 'start',
          cb: onStart,
        },
        {
          name: 'lookAt',
          cb: onLookAt,
        },
        {
          name: 'follow',
          cb: onFollow,
        },
        {
          name: 'scroll',
          cb: onWheel,
        },
      ]

      if (canvasRef.current) {
        speApp = new Application(canvasRef.current, { autoRender })

        async function init() {
          await speApp.load(scene)

          for (const event of events) {
            if (event.cb) {
              speApp.addEventListener(event.name, event.cb)
            }
          }

          setIsLoading(false)
          onLoad?.(speApp)
        }

        init()
      }

      return () => {
        for (const event of events) {
          if (event.cb) {
            speApp.removeEventListener(event.name, event.cb)
          }
        }
        speApp.dispose()
      }
      // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [scene])

    return (
      <Canvas
        ref={mergeRefs<HTMLCanvasElement>([ref, canvasRef])}
        style={style}
        {...props}
      />
    )
  },
)

export default Spline

Now I'm getting

ReferenceError: Can't find variable: document

and

TypeError: undefined is not an object (evaluating '_$$_REQUIRE(_dependencyMap[7], "@splinetool/runtime").Application')

It seems I'm going down a rabbit hole. Is there a better way?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions