diff --git a/lib/hooks/invite-links.js b/lib/hooks/invite-links.js
new file mode 100644
--- /dev/null
+++ b/lib/hooks/invite-links.js
@@ -0,0 +1,105 @@
+// @flow
+
+import React from 'react';
+
+import {
+  createOrUpdatePublicLink,
+  createOrUpdatePublicLinkActionTypes,
+  disableInviteLink as callDisableInviteLink,
+  disableInviteLinkLinkActionTypes,
+} from '../actions/link-actions.js';
+import { createLoadingStatusSelector } from '../selectors/loading-selectors.js';
+import type { SetState } from '../types/hook-types.js';
+import type { InviteLink } from '../types/link-types.js';
+import {
+  useDispatchActionPromise,
+  useServerCall,
+} from '../utils/action-utils.js';
+import { useSelector } from '../utils/redux-utils.js';
+
+const createOrUpdatePublicLinkStatusSelector = createLoadingStatusSelector(
+  createOrUpdatePublicLinkActionTypes,
+);
+const disableInviteLinkStatusSelector = createLoadingStatusSelector(
+  disableInviteLinkLinkActionTypes,
+);
+
+function useInviteLinksActions(
+  communityID: string,
+  inviteLink: ?InviteLink,
+): {
+  +error: ?string,
+  +isLoading: boolean,
+  +name: string,
+  +setName: SetState<string>,
+  +createOrUpdateInviteLink: () => mixed,
+  +disableInviteLink: () => mixed,
+} {
+  const [name, setName] = React.useState(
+    inviteLink?.name ?? Math.random().toString(36).slice(-9),
+  );
+  const [error, setError] = React.useState(null);
+  const dispatchActionPromise = useDispatchActionPromise();
+
+  const callCreateOrUpdatePublicLink = useServerCall(createOrUpdatePublicLink);
+  const createCreateOrUpdateActionPromise = React.useCallback(async () => {
+    setError(null);
+    try {
+      return await callCreateOrUpdatePublicLink({
+        name,
+        communityID,
+      });
+    } catch (e) {
+      setError(e.message);
+      throw e;
+    }
+  }, [callCreateOrUpdatePublicLink, communityID, name]);
+  const createOrUpdateInviteLink = React.useCallback(() => {
+    dispatchActionPromise(
+      createOrUpdatePublicLinkActionTypes,
+      createCreateOrUpdateActionPromise(),
+    );
+  }, [createCreateOrUpdateActionPromise, dispatchActionPromise]);
+
+  const disableInviteLinkServerCall = useServerCall(callDisableInviteLink);
+  const createDisableLinkActionPromise = React.useCallback(async () => {
+    setError(null);
+    try {
+      return await disableInviteLinkServerCall({
+        name,
+        communityID,
+      });
+    } catch (e) {
+      setError(e.message);
+      throw e;
+    }
+  }, [disableInviteLinkServerCall, communityID, name]);
+  const disableInviteLink = React.useCallback(() => {
+    dispatchActionPromise(
+      disableInviteLinkLinkActionTypes,
+      createDisableLinkActionPromise(),
+    );
+  }, [createDisableLinkActionPromise, dispatchActionPromise]);
+  const disableInviteLinkStatus = useSelector(disableInviteLinkStatusSelector);
+
+  const createOrUpdatePublicLinkStatus = useSelector(
+    createOrUpdatePublicLinkStatusSelector,
+  );
+  const isLoading =
+    createOrUpdatePublicLinkStatus === 'loading' ||
+    disableInviteLinkStatus === 'loading';
+
+  return React.useMemo(
+    () => ({
+      error,
+      isLoading,
+      name,
+      setName,
+      createOrUpdateInviteLink,
+      disableInviteLink,
+    }),
+    [createOrUpdateInviteLink, disableInviteLink, error, isLoading, name],
+  );
+}
+
+export { useInviteLinksActions };
diff --git a/native/invite-links/manage-public-link-screen.react.js b/native/invite-links/manage-public-link-screen.react.js
--- a/native/invite-links/manage-public-link-screen.react.js
+++ b/native/invite-links/manage-public-link-screen.react.js
@@ -3,20 +3,10 @@
 import * as React from 'react';
 import { Text, View, Alert } from 'react-native';
 
-import {
-  createOrUpdatePublicLink,
-  createOrUpdatePublicLinkActionTypes,
-  disableInviteLink as callDisableInviteLink,
-  disableInviteLinkLinkActionTypes,
-} from 'lib/actions/link-actions.js';
 import { inviteLinkUrl } from 'lib/facts/links.js';
+import { useInviteLinksActions } from 'lib/hooks/invite-links.js';
 import { primaryInviteLinksSelector } from 'lib/selectors/invite-links-selectors.js';
-import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js';
 import type { ThreadInfo } from 'lib/types/thread-types.js';
-import {
-  useDispatchActionPromise,
-  useServerCall,
-} from 'lib/utils/action-utils.js';
 
 import Button from '../components/button.react.js';
 import TextInput from '../components/text-input.react.js';
@@ -37,34 +27,14 @@
 function ManagePublicLinkScreen(props: Props): React.Node {
   const { community } = props.route.params;
   const inviteLink = useSelector(primaryInviteLinksSelector)[community.id];
-  const [name, setName] = React.useState(
-    inviteLink?.name ?? Math.random().toString(36).slice(-9),
-  );
-  const [error, setError] = React.useState(null);
-
-  const dispatchActionPromise = useDispatchActionPromise();
-  const callCreateOrUpdatePublicLink = useServerCall(createOrUpdatePublicLink);
-  const createCreateOrUpdateActionPromise = React.useCallback(async () => {
-    setError(null);
-    try {
-      return await callCreateOrUpdatePublicLink({
-        name,
-        communityID: community.id,
-      });
-    } catch (e) {
-      setError(e.message);
-      throw e;
-    }
-  }, [callCreateOrUpdatePublicLink, community.id, name]);
-  const createInviteLink = React.useCallback(() => {
-    dispatchActionPromise(
-      createOrUpdatePublicLinkActionTypes,
-      createCreateOrUpdateActionPromise(),
-    );
-  }, [createCreateOrUpdateActionPromise, dispatchActionPromise]);
-  const createOrUpdatePublicLinkStatus = useSelector(
-    createOrUpdatePublicLinkStatusSelector,
-  );
+  const {
+    error,
+    isLoading,
+    name,
+    setName,
+    createOrUpdateInviteLink,
+    disableInviteLink,
+  } = useInviteLinksActions(community.id, inviteLink);
 
   const styles = useStyles(unboundStyles);
 
@@ -73,31 +43,6 @@
     errorComponent = <Text style={styles.error}>{error}</Text>;
   }
 
-  const disableInviteLinkServerCall = useServerCall(callDisableInviteLink);
-  const createDisableLinkActionPromise = React.useCallback(async () => {
-    setError(null);
-    try {
-      return await disableInviteLinkServerCall({
-        name,
-        communityID: community.id,
-      });
-    } catch (e) {
-      setError(e.message);
-      throw e;
-    }
-  }, [disableInviteLinkServerCall, community.id, name]);
-  const disableInviteLink = React.useCallback(() => {
-    dispatchActionPromise(
-      disableInviteLinkLinkActionTypes,
-      createDisableLinkActionPromise(),
-    );
-  }, [createDisableLinkActionPromise, dispatchActionPromise]);
-  const disableInviteLinkStatus = useSelector(disableInviteLinkStatusSelector);
-
-  const isLoading =
-    createOrUpdatePublicLinkStatus === 'loading' ||
-    disableInviteLinkStatus === 'loading';
-
   const onDisableButtonClick = React.useCallback(() => {
     Alert.alert(
       'Disable public link',
@@ -162,7 +107,7 @@
         {errorComponent}
         <Button
           style={[styles.button, styles.buttonPrimary]}
-          onPress={createInviteLink}
+          onPress={createOrUpdateInviteLink}
           disabled={isLoading}
         >
           <Text style={styles.buttonText}>Save & enable public link</Text>
@@ -173,13 +118,6 @@
   );
 }
 
-const createOrUpdatePublicLinkStatusSelector = createLoadingStatusSelector(
-  createOrUpdatePublicLinkActionTypes,
-);
-const disableInviteLinkStatusSelector = createLoadingStatusSelector(
-  disableInviteLinkLinkActionTypes,
-);
-
 const unboundStyles = {
   sectionTitle: {
     fontSize: 14,