Basics Signal is a reactive value which re-renders the component when it is changed signal.value consumed by component re-renders the component if we consume signal directly in jsx, it will just change the dom without component re-render to update the signal you just have to mutate the signal.value prop, that's simple https://preactjs.com/guide/v10/signals/ https://github.com/preactjs/signals/tree/main/packages/react#react-integration npm install @preact/signals-react to let it signals without react hooks some magic is done on compilation step, thus we need to add Babel transformation npm i --save-dev @preact/signals-react-transform Vite config Add ["module:@preact/signals-react-transform"] into babel plugins
/// <reference types="vitest" />
/// <reference types="vite/client" />
import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react'
import tsconfigPaths from 'vite-tsconfig-paths'
// https://vitejs.dev/config/
export default defineConfig(({ command, mode }) => {
// Load env file based on `mode` in the current working directory.
// Set the third parameter to '' to load all env regardless of the `VITE_` prefix.
const env = loadEnv(mode, process.cwd(), '')
return {
server: {
port: Number(env.PORT_FRONT_END),
proxy: {
// '/api': `${process.env.DOMAIN}:${process.env.PORT_BACK_END}/`
'/api': `${env.DOMAIN}:${env.PORT_BACK_END}`,
},
// hmr: {
// host: 'localhost',
// port: Number(env.PORT_BACK_END),
// }
},
esbuild: {
define: {
// to suppress warning in terminal: [vite] warning: Top-level "this" will be replaced with undefined since this file is an ECMAScript module
this: 'window',
},
},
plugins: [
react({
// to show readable class names in styled components with vite
// https://github.com/styled-components/babel-plugin-styled-components/issues/350#issuecomment-979873241
jsxImportSource: '@emotion/react',
babel: {
plugins: [
[
// 'babel-plugin-styled-components',
'@emotion/babel-plugin',
{
displayName: true,
fileName: true,
},
],
// https://github.com/preactjs/signals/tree/main/packages/react#react-integration
["module:@preact/signals-react-transform"],
],
},
}),
// https://github.com/aleclarson/vite-tsconfig-paths
tsconfigPaths(),
],
// https://vitest.dev/guide/in-source.html
define: {
'import.meta.vitest': 'undefined',
},
// https://www.youtube.com/watch?v=oWJpxtAl62w
test: {
globals: true,
environment: 'jsdom',
setupFiles: './test-setup.ts',
includeSource: ['client/**/*.{js,ts,jsx,tsx}'],
coverage: {
all: true,
src: ['client/'],
},
},
build: {
outDir: 'build',
},
}
})
Example of reactive global state
// bottomMessageSignal.ts
import { signal } from '@preact/signals-react'
export const bottomMessage = signal('')
export const showBottomMessage = (msg: string): void => {
bottomMessage.value = msg
}
export const hideBottomMessage = (): void => {
bottomMessage.value = ''
}
// BottomMessage.tsx
import { AnimatePresence, motion } from 'framer-motion'
import { bottomMessage } from './bottomMessageSignal'
export const BottomMessage = (): JSX.Element => {
return (
<AnimatePresence>
{bottomMessage.value !== '' && (
<motion.span
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 20 }}
transition={{ duration: 0.5 }}
css={{
position: 'fixed',
bottom: 5,
right: 5,
fontSize: 14,
color: '#828282',
fontWeight: 500,
userSelect: 'none',
}}
>
{bottomMessage.value}
</motion.span>
)}
</AnimatePresence>
)
}