Trying out signals in Vaadin 24.4 beta1

We recently released Vaadin 24.4.0.beta1. One of the new features that we’re introducing is support for doing UI state management using signals.

Here’s how you can try that out right now. Start by downloading https://start.vaadin.com/dl?preset=react&preset=partial-prerelease, extracting it, importing it to an IDE, and running the main method in Application.java.

You can and should use signals from @vaadin/hilla-react-signals for state management in Hilla views. As an example, you can replace the content of @index.tsx with a button that counts how many times it has been clicked.

src/main/frontend/views/@index.tsx

import { useSignal } from "@vaadin/hilla-react-signals";
import { Button } from "@vaadin/react-components";

export default function ClickCount() {
    const count = useSignal(0);
    return <Button onClick={() => count.value++}>Click count: {count}</Button>
}

The useSignal hook is a wrapper around useMemo to create a signal instance that is bound to the life cycle of the rendered React component instance.

Sharing signal instances

You can also define a signal with a longer life cycle but you should then be careful to do it outside the React render function so that you don’t create a new instance every time the component is rendered. You should use the signal(<initial value>) factory function rather than the useSignal(<initial value>) hook when creating signal instances outside React render functions.

Furthermore, you can export the signal instances so that they can be used by other client-side parts of the UI.

src/main/frontend/views/@index.tsx

import { signal } from "@vaadin/hilla-react-signals";
import { Button } from "@vaadin/react-components";

export const count = signal(0);

export default function ClickCount() {
    return <Button onClick={() => count.value++}>Click count: {count}</Button>
}

This signal instance can then be imported in some other component, e.g. the main layout. (This is a nonsensical example since it makes the layout depend on a specific view but it’s still a simple way of showing the point.)

You can edit the main layout to show the click count next to the title of the current view. (Not showing the full file here since it’s long-ish.)

src/main/frontend/views/@layout.tsx (partial)

import { count } from './@index';

      // Further down inside the render function:
      <h2 slot="navbar" className="text-l m-0">
        {currentTitle} {count}
      </h2>
4 Likes

hi, question here. I have the following:

export default function VersionCard({cifolder}: { cifolder: CIFolder }) {

    const selectedVersion = useSignal<CIVersion[]>([]);
    ...
    return <>...</>
}

the component is refreshed when the parameter cifolder changes but the signal keeps the previous value. Is this intentional? How do I ‘reset’ the signal when the VersionCard component is rerendered?

thanks

The code snippet you shared only shows a signal created with [] as its initial value. There’s nothing in the code that you shared that would ever change the value of that signal (that would be a selectedVersion.value = <something> line somewhere) and there’s also nothing in the code that you shared that uses the cifolder variable for anything.