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