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?

Activity

  1. locked and limited conversation to collaborators on Aug 25, 2022
  2. converted this issue into a discussion #77 on Aug 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions