Skip to content

Changing <Model /> source prop causes app crash on Expo react-native application #323

@tdkwan

Description

@tdkwan

I've been having difficulty using react-native expo and react-native-filament to display 3d models in succession within the same display component<ModelViewer>.

When changing the source prop of a <Model /> component from react-native-filament, React Native throws the following warning and eventually crashes on subsequent model loads:

Warning: Error: Regular javascript function '' cannot be shared. 
Try decorating the function with the 'worklet' keyword to allow the javascript function to be used as a worklet.

This occurs when re-rendering my ModelViewer component which wraps FilamentScene, FilamentView, and Model to display a different .glb file.


Environment

Library Version
react-native 0.79.6
expo 53.0.23
react-native-filament 1.8.0
react-native-reanimated 3.17.4
Device iOS Simulator (M4 IPad Pro)

Steps to Reproduce

  1. Create a ModelViewer component that loads a .glb asset using react-native-filament.
  2. Change the modelPath prop at runtime to a different .glb file.
  3. Observe that the app throws a Regular javascript function '' cannot be shared error during the update cycle.
// ModelViewer.tsx
import React, { useState, useEffect } from "react";
import { FilamentView, Model } from "react-native-filament";

interface ModelViewerProps {
  modelPath: string;
  key?: string;
}

export default function ModelViewer({ modelPath, key }: ModelViewerProps) {
  const [actualModelPath, setActualModelPath] = useState<string>("");
  const [isLoading, setIsLoading] = useState(true);
  const [hasError, setHasError] = useState(false);

  useEffect(() => {
    setActualModelPath(modelPath);
    setIsLoading(false);
  }, [modelPath]);

  if (isLoading || hasError) return null;

  return (
    <FilamentScene key={key}>
      <FilamentView key={key} style={styles.filamentView}>
        <DefaultLight />
        <Model 
          key={key}
          translate={[0, 0, -5]} 
          source={{uri: actualModelPath}} 
        />
        <Camera />
      </FilamentView>
    </FilamentScene>
  );
}

Then render it in a parent component and update the prop:

// Parent.tsx
import {Asset} from 'expo-asset'
const [selectedModel, setSelectedModel] = Asset.fromModule('../assets/model/watch.glb').uri;

return (
  <>
    <Button title="Switch" onPress={() => setSelectedModel(Asset.fromModule('../assets/model/rhino.glb').uri} />
    <ModelViewer modelPath={selectedModel} />
  </>
);

Fix Attempts

  • Adding to .glb to the metro.config.js
  • I thought this might be an issue related to how I was loading my .glb assets, and the speed at which I was accessing them, so I modified my code to use different file system libraries.
    • expo-asset
      • I moved my assets into /assets/models/*.glb and tried to load them using Assets.fromModule(require('../assets/models/rhino.glb')).uri
    • react-native-fs
      • I served my assets from a local server and downloaded them to the device's Documents/ folder and downloaded them at runtime.
    • expo-file-system
      • (Same as above)
  • Reducing the file of the .glb files. This worked partially. However, I found that though the 'worklet' issue wouldn't appear, there would be a file read error saying something like: unexpected character ‘’ error
  • Wrapping the Model in a 'worklet' function
  • Downgrading my dependencies as per NSException crash

Minimal Reproduction Repo

You can reproduce it by cloning this example repo and switching between two .glb files using a state toggle.


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