Skip to content

TypeScript React WebSocket Hooks

You can generate React hooks for interacting with your API from a React website by adding the following options to your TypeSafeWebSocketApiProject in your .projenrc:

new TypeSafeWebSocketApiProject({
  library: {
    libraries: [WebSocketLibrary.TYPESCRIPT_WEBSOCKET_HOOKS],
  },
  ...
});
TypeSafeWebSocketApiProject.Builder.create()
        .library(WebSocketLibraryConfiguration.builder()
                .libraries(Arrays.asList(WebSocketLibrary.TYPESCRIPT_WEBSOCKET_HOOKS))
                .build())
        ...
        .build();
TypeSafeWebSocketApiProject(
    library=WebSocketLibraryConfiguration(
        libraries=[WebSocketLibrary.TYPESCRIPT_WEBSOCKET_HOOKS]
    )
    ...
)

Usage in a React Website

First, make sure you add a dependency on the generated hooks library. This is done automatically if you pass your TypeSafeWebSocketApiProject into the CloudscapeReactTsWebsite, eg in your .projenrc:

const api = new TypeSafeApiProject({ ... });

new CloudscapeReactTsWebsite({
  ...,
  typeSafeWebSocketApis: [api],
});
TypeSafeApiProject api = new TypeSafeWebSocketApiProject(TypeSafeWebSocketApiProjectOptions.builder()
        ...
        .build();

CloudscapeReactTsWebsite.Builder.create()
        ...
        .typeSafeWebSocketApis(Arrays.asList(api))
        .build();
api = TypeSafeWebSocketApiProject(...)

CloudscapeReactTsWebsite(
    ...
    type_safe_web_socket_apis=[api]
)

Note

If you are not using CloudscapeReactTsWebsite, you can add the dependency manually using api.library.typescriptWebSocketHooks!.package.packageName. You will also need to depend on api.library.typescriptWebSocketClient.

Make sure to run npx projen to synthesize your .projenrc changes.

Next, create an instance of the API client from the WebSocketLibrary.TYPESCRIPT_WEBSOCKET_CLIENT library in your React Website (making sure to set the url and credentials). See the WebSocket Clients documentation for more details about configuring the client.

Note that if you are using the CloudscapeReactTsWebsite, a hook will already be configured for you which manages instantiating the client for IAM (Sigv4) Authentication.

A simple hook to create the client might look as follows:

import { DefaultApiWebSocketClient } from "myapi-typescript-websocket-client";

export const useWebSocketApiClient = () => {
  // Use a ref to ensure that if the effect is retriggered, we do not create a second connection
  const clientPromiseRef = useRef<Promise<DefaultApiWebSocketClient> | null>(null);
  const [client, setClient] = useState<DefaultApiWebSocketClient>();

  useEffect(() => {
    if (clientPromiseRef.current) {
      return;
    }
    void (async () => {
      clientPromiseRef.current = DefaultApiWebSocketClient.connect(...);
      setClient(await clientPromiseRef.current!);
    })();
  }, [setClient, clientPromiseRef]);

  return client;
};

Next, instantiate the client provider above where you would like to use the hooks in your component hierarchy (such as above your router). For example:

// NB: client provider may be named differently if you have tagged your operations
import { DefaultApiWebSocketClientProvider } from "myapi-typescript-websocket-hooks";

const client = useWebSocketApiClient();

return client ? (
  <DefaultApiClientProvider client={client}>
    {/* Components within the provider may make use of the hooks */}
  </DefaultApiClientProvider>
) : <Spinner />;

Finally, you can import and use your generated hooks. For example:

import { useDefaultApiWebSocketClient, useOnSendNotification } from "myapi-typescript-websocket-hooks";

export const MyComponent: FC<MyComponentProps> = () => {
  // The useDefaultApiWebSocketClient hook retrieves the client from the context (reusing the existing connection)
  const client = useDefaultApiWebSocketClient();

  const [notifications, setNotifications] = useState<string[]>([]);

  // The useOn<OperationName> hooks listen for server_to_client or bidirectional messages for the operation
  // Pass the deps array as the second argument just like you would for a useCallback hook.
  useOnSendNotification((input) => {
    setNotifications(prev => [...prev, input.message]);
  }, [setNotifications]);

  return (
    <div>
      {/* Use the client to send messages to the server */}
      <Button onClick={() => client.subscribeToNotifications({ topic: "foo" })}>Send!</Button>
      <ul>
        {notifications.map((notification, i) => (
          <li key={i}>
            {notification}
          </li>
        ))}
      </ul>
    </div>
  )
};

Last update: 2024-05-08