How to Set Up Vite React Signal
How to Set Up Vite React Signal
Repo
github.com/KrisNathan/vite-react-signal-hono
Why
Signals offer a way for developers to manage state in the most efficient way possible with ease.
Signals are able to offer efficiency by only rerendering what needs to be rerendered unlike legacy react useContext
.
Such ease is made possible by the excellent ergonomics provided by the automatic state binding and dependency tracking.
Vite + React Setup
Skip this if you already have a vite+react set up.
npm create vite@latest
- Select React
- Select Typescript
Steps
Here’s how you can set up vite + react with preact signals.
Install the dependencies
npm i @preact/signals-react
npm i -D @preact/signals-react-transform
At the time of writing here are the versions of those libraries:
- @preact/[email protected]
- @preact/[email protected]
Next, we need to configure babel
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
// https://vite.dev/config/
export default defineConfig({
plugins: [
react({
babel: {
plugins: [["module:@preact/signals-react-transform"]], // +
},
}),
],
});
Usage
We can declare and initialize our state this way:
// src/signals/counter.ts
import { signal } from "@preact/signals-react";
export const counter = signal(0);
To update the state we can simply just modify the .value
property.
Here’s an example in the form of counter button.
// src/CounterBtn.tsx
import React from "react";
import { counter } from "./signals/counter";
interface CounterBtnProps {
children: React.ReactNode;
}
export default function CounterBtn({ children }: CounterBtnProps) {
return (
<button
onClick={() => {
counter.value++;
}}
>
{children}
</button>
);
}
To render the state values we can just directly access .value
// src/Label.tsx
import { counter } from "./signals/counter";
export default function Label() {
return <p>count is {counter.value}</p>;
}
Caveats
At the time of writing signals-react doesn’t support passing signals as props.
Such usage isn’t recommended:
import { counter } from "./signals/counter";
export default function Page() {
return <SomeComponent count={counter.value} />;
}
This can be a problem if you use a component library where you can’t modify it directly and import the signal directly.
Workaround
However, theres a neat workaround that can be done.
wrappers
Pretend that the library contains the following:
// src/Label.tsx
interface LabelProps {
count: number;
}
// simulate external component library that only accepts prop
// we can't directly import the signal
export default function Label({ count }: LabelProps) {
return <p>count is {count}</p>;
}
We can simply wrap it around another component.
// src/LabelWrapper.tsx
import Label from "./Label";
import { counter } from "./signals/counter";
// simulate external component library that only accepts prop
// we can't directly import the signal
const LabelWrapper = () => <Label count={counter.value} />;
export default LabelWrapper;