-
Notifications
You must be signed in to change notification settings - Fork 88
(DOCSP-27000): Call a Function for React Native #2535
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,80 @@ | ||||||
// :snippet-start: call-function | ||||||
import React from 'react'; | ||||||
import {useUser} from '@realm/react'; | ||||||
// :remove-start: | ||||||
import {Credentials} from 'realm'; | ||||||
import {useEffect, useState} from 'react'; | ||||||
import {App} from 'realm'; | ||||||
import {AppProvider, UserProvider, useApp} from '@realm/react'; | ||||||
import {render, fireEvent, waitFor} from '@testing-library/react-native'; | ||||||
import {View, Button, Text} from 'react-native'; | ||||||
|
||||||
const APP_ID = 'example-testers-kvjdy'; | ||||||
|
||||||
function AppWrapper() { | ||||||
return ( | ||||||
<View> | ||||||
<AppProvider id={APP_ID}> | ||||||
<MyApp /> | ||||||
</AppProvider> | ||||||
</View> | ||||||
); | ||||||
} | ||||||
|
||||||
function MyApp() { | ||||||
const [loggedIn, setLoggedIn] = useState(false); | ||||||
const app = useApp(); | ||||||
|
||||||
useEffect(() => { | ||||||
app.logIn(Credentials.anonymous()).then(user => user && setLoggedIn(true)); | ||||||
}, []); | ||||||
// ... | ||||||
return loggedIn ? ( | ||||||
<View> | ||||||
<UserProvider> | ||||||
<Text>Foo!</Text> | ||||||
<Addition /> | ||||||
</UserProvider> | ||||||
</View> | ||||||
) : null; | ||||||
} | ||||||
|
||||||
let higherScopedSum: number; | ||||||
// :remove-end: | ||||||
|
||||||
function Addition() { | ||||||
// Get currently logged in user | ||||||
const user = useUser(); | ||||||
|
||||||
const addNumbers = async (numA: number, numB: number) => { | ||||||
// Call Atlas Function | ||||||
|
||||||
// Method 1: call with User.callFunction() | ||||||
const sumMethod1 = await user?.callFunction('sum', numA, numB); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
I need to get a fix out for this immediately. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, i'd rather keep this example as is til that's fixed since the code will throw a TS error if missing |
||||||
|
||||||
// Method 2: Call with User.function.<Function name>() | ||||||
const sumMethod2 = await user?.functions.sum(numA, numB); | ||||||
|
||||||
// Both methods return the same result | ||||||
console.log(sumMethod1 === sumMethod2); // true | ||||||
// :remove-start: | ||||||
expect(sumMethod1).toBe(sumMethod2); | ||||||
higherScopedSum = sumMethod1 as number; | ||||||
// :remove-end: | ||||||
}; | ||||||
// ... | ||||||
// :remove-start: | ||||||
return <Button onPress={() => addNumbers(1, 2)} testID='test-function-call' title='Test Me!' />; | ||||||
// :remove-end: | ||||||
} | ||||||
// :snippet-end: | ||||||
|
||||||
afterEach(async () => await App.getApp(APP_ID).currentUser?.logOut()); | ||||||
|
||||||
test('Call Atlas Function', async () => { | ||||||
const {getByTestId} = render(<AppWrapper />); | ||||||
|
||||||
const button = await waitFor(() => getByTestId('test-function-call')); | ||||||
fireEvent.press(button); | ||||||
await waitFor(() => expect(higherScopedSum).toBe(3)); | ||||||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import React from 'react'; | ||
import {useUser} from '@realm/react'; | ||
|
||
function Addition() { | ||
// Get currently logged in user | ||
const user = useUser(); | ||
|
||
const addNumbers = async (numA: number, numB: number) => { | ||
// Call Atlas Function | ||
|
||
// Method 1: call with User.callFunction() | ||
const sumMethod1 = await user?.callFunction('sum', numA, numB); | ||
|
||
// Method 2: Call with User.function.<Function name>() | ||
const sumMethod2 = await user?.functions.sum(numA, numB); | ||
Comment on lines
+14
to
+15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my opinion, this would be the preferred syntax. But we need to show an example of how to type this. This can be done on the hook itself.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'm confused by the code example and how it relates to calling a function. could you explain a bit more? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you declare the FunctionTypes as so:
Then
The thing is, it is currently not possible to infer what a user has configured in App Services, so they would have to define the types themselves in order to have proper typing in their application. Ideally we should have a way to generate this for our users, but for now, this is the way. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not very well documented. There is an example of this in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. got it, thanks for clarifying. this is quite useful. will include in the docs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, after spending a bit of time with this, it seems that the API is slightly different from what's in the above example. these differences are:
type FunctionTypes = {
sum: (a: number, b: number) => number;
} & Realm.DefaultFunctionsFactory; for these reasons, i'd rather not include this in the docs yet, though i think longer-term it absolutely makes sense to include in the docs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you can see the working version with the types in this commit checkpoint: RN types on useUser. going to revert those changes in the HEAD of the branch. |
||
|
||
// Both methods return the same result | ||
console.log(sumMethod1 === sumMethod2); // true | ||
}; | ||
// ... | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can also provide a
fallback
component to theUserProvider
. Then your wrapper above would look like so:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The UserProvider will not render its children until there is a current user set on the app object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah it took me an hour+ of debugging to figure that out 😅
since this code isn't in the example i don't think we need to include that here.
tho when we @realm/reactify the auth pages, this pattern will be important to explain (cc @krollins-mdb)