Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose worklet runtime #601

Merged
merged 3 commits into from
Jan 22, 2025
Merged

Expose worklet runtime #601

merged 3 commits into from
Jan 22, 2025

Conversation

tomekzaw
Copy link
Collaborator

@tomekzaw tomekzaw commented Jan 20, 2025

Details

When you write to a shared value in the RN runtime using sv.value = 42; or sv.value(42); syntax, the call is scheduled to be run on the UI runtime. This means that the shared value doesn't update its value on other worklet runtimes.

As a workaround, instead of calling sv.value = 42 in the RN runtime which simply calls runOnUI under the hood, you can call runOnRuntime on your own like this:

runOnRuntime(workletRuntime, () => {
  'worklet';
  sv.value = 42;
})();

However, currently there's no way to obtain workletRuntime. This PR exposes workletRuntime used by MarkdownTextInput component using getWorkletRuntime() function.

Because worklet runtime creation is lazy (since we really shouldn't call native/turbo modules in the top-level scope so we delay the creation till MarkdownTextInput is first rendered), please make sure to call getWorkletRuntime() function only when the worklet runtime has already been initialized. This means you cannot call getWorkletRuntime() in top-level scope of your code. Also, please make sure not to memoize or even store its result since since this affects the lifetime of WorkletRuntime instance which should be managed only by react-native-live-markdown. Finally, please do not call getWorkletRuntime() on web where parsers are run directly on the same JS runtime and there's no concept of worklet runtimes.

As for runOnRuntime, this function is sadly not available on web. Also, keep in mind that runOnRuntime(worklet) itself doesn't schedule the worklet to be run; it just creates a JS function that, when called, will schedule the call. So if you want to immediately schedule the worklet, please use the following syntax: runOnRuntime(() => { ... }))(); with () at the end of the line.

Related Issues

GH_LINK

Manual Tests

App.tsx

import * as React from 'react';
import {Button, StyleSheet, View} from 'react-native';
import {runOnRuntime, useSharedValue} from 'react-native-reanimated';

import {
  getWorkletRuntime,
  MarkdownTextInput,
  parseExpensiMark,
} from '@expensify/react-native-live-markdown';

export default function App() {
  const sv = useSharedValue('before');

  React.useEffect(() => {
    runOnRuntime(getWorkletRuntime(), () => {
      'worklet';
      sv.value = 'after';
    })();
  }, [sv]);

  return (
    <View style={styles.container}>
      <MarkdownTextInput parser={parseExpensiMark} />
      <Button
        title="Press me"
        onPress={() => {
          runOnRuntime(getWorkletRuntime(), () => {
            'worklet';
            console.log(sv.value);
          })();
        }}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
});

Linked PRs

Copy link
Member

@Kicu Kicu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes a real life problem when using workletized parser in LiveMarkdown and I saw it working 👍

I have one question though. In your examples we pass an anonymous function onPress like this:

() => {runOnRuntime(workletRuntime, () => {
  'worklet';
  sv.value = 42;
})()
};

But runOnRuntime already returns a function, so in onPress could we also write code like this?

onPress={runOnRuntime(workletRuntime, () => {
  'worklet';
  sv.value = 42;
})}

I think in useEffect we might still need the outer wrapper because React likes it that way (though I'm a bit unsure 🤔) but in onPress it should be ok

src/MarkdownTextInput.tsx Outdated Show resolved Hide resolved
@tomekzaw
Copy link
Collaborator Author

But runOnRuntime already returns a function, so in onPress could we also write code like this?

@Kicu I often ask myself this question. onPress callback accepts event argument so if we pass runOnRuntime(...) directly to onPress then we will actually call runOnRuntime(...)(event) which will result in serializing event. That's why event handlers are usually lambdas with no arguments.

@Kicu
Copy link
Member

Kicu commented Jan 21, 2025

Totally makes sense 👍

@tomekzaw tomekzaw marked this pull request as ready for review January 21, 2025 08:15
@tomekzaw tomekzaw requested a review from Skalakid January 21, 2025 08:15
Skalakid
Skalakid previously approved these changes Jan 21, 2025
Copy link
Collaborator

@Skalakid Skalakid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Member

@Kicu Kicu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@tomekzaw tomekzaw requested a review from Skalakid January 21, 2025 15:15
@tomekzaw tomekzaw merged commit 3a1e565 into main Jan 22, 2025
5 checks passed
@tomekzaw tomekzaw deleted the @tomekzaw/expose-worklet-runtime branch January 22, 2025 07:52
@os-botify
Copy link
Contributor

os-botify bot commented Jan 22, 2025

🚀 Published to npm in 0.1.222 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants