Writing a Browser Greeter Client

4 min read

This page shows how to write a Greeter client that runs in a web browser, as a React application written in TypeScript. It calls the same greet operation as the Node.js client; it differs in its transport, in how it creates and destroys its communicator, and in how you build and run it.

You can find the complete source code for this client in the ice-demos repository.

The client is built with Vite. The @zeroc/slice2js package provides a Vite plug-in that runs slice2js on Greeter.ice during the build, and generates Greeter.js and the TypeScript declarations in Greeter.d.ts:

TypeScript
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import slice2js from "@zeroc/slice2js/unplugin/vite";
export default defineConfig({
plugins: [
react(),
slice2js({
inputs: ["src/Greeter.ice"],
args: ["--typescript"],
outputDir: "src",
}),
],
});

See Using the Slice Compiler for the plug-ins and the slice2js options.

The client is a React component, GreeterApp, with a form: you enter a name, click Send Greeting, and the component displays the greeting returned by the server.

A browser application has no command line, so the client creates its communicator without arguments. It creates the communicator and the proxy once, when the component mounts, and keeps them in refs so that each click reuses them:

TypeScript
const communicatorRef = useRef<Ice.Communicator | null>(null);
const greeterRef = useRef<VisitorCenter.GreeterPrx | null>(null);
useEffect(() => {
communicatorRef.current = new Ice.Communicator();
greeterRef.current = new VisitorCenter.GreeterPrx(
communicatorRef.current,
"greeter:ws -h localhost -p 4061",
);
return () => {
communicatorRef.current?.destroy();
};
}, []);

The stringified proxy uses the ws transport: in a browser, Ice for JavaScript supports only the ws and wss transports.

When you submit the form, the component calls greet on the proxy and awaits the greeting:

TypeScript
const sendGreeting = useCallback(async () => {
try {
const greeting = await greeterRef.current!.greet(state.name.trim());
// Display the greeting.
...
} catch (error) {
// Display the error, for example when no server is listening on port 4061.
...
}
}, [state.name]);

The function returned by the useEffect callback runs when the component unmounts: it destroys the communicator, which closes its connections.

Ice for JavaScript has limited server-side support, so the client calls a Greeter server written in another language. The Python Config server of the demos hosts a Greeter object with identity greeter, and its object adapter listens on port 4061 with the transport set by Ice.Default.Protocol. The server is a uv project. From the root of an ice-demos 3.8 checkout, start it with the ws transport:

Shell
cd python/Ice/config/server
uv run slice2py ../slice/Greeter.ice
uv run main.py --Ice.Default.Protocol=ws

Then, in another terminal, install the client's dependencies and start the Vite development server:

Shell
cd js/Ice/react-greeter
npm install
npm run dev

Open http://localhost:5173 in your browser and click Send Greeting. The page shows the greeting returned by the server: Hello, React!.