0.4.1 • Published 1 year ago

@mdekrey/jotai-react-signals v0.4.1

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

React Signals with Jotai

This provides a simple way to bind Jotai atoms to React intrinsic elements to get reactive pages, like the signals paradigm.

Please note that the current state of this package is a proof-of-concept; while it works quite well, there is no live demo and the API may change significantly.

Included in this package:

  • Hooks for more easily working with computed Jotai state
    • useComputedAtom - same interface as Jotai's computed, but as a React hook with no dependencies
    • useAsAtom - converts a T | Atom<T> to an Atom<T> as a React hook, so your APIs can be flexible.
  • Utilities for animation
    • animationSignal - provides updates per requestAnimationFrame (must subscribe to the animationSignal or call manuallyUpdateAnimationFrame)
    • manuallyUpdateAnimationFrame - function to manually trigger animationSignal updates when not subscribed (this prety well defeats the purpose of animations...)
    • tweenedSignal - tweens an Atom<number> via an EasingFunction (not provided, but see tween.js's Easing export for examples) using the animationSignal
  • Utilities for binding to elements
    • withSignal - takes the name of a React intrinsic element and a mapping of React props to functions to update the element to create a new component that will update your element in real-time.
    • mapProperty - helper function for mapping a value to an element property
    • mapAttribute - helper function for mapping a value to an element attribute

Usage example, as a Storybook entry:

import { Story, Meta } from '@storybook/react';
import { useMemo } from 'react';
import { useStore } from 'jotai';
import {
	tweenedSignal,
	useComputedAtom,
	mapAttribute,
	type PartialMapping,
	withSignal,
	useAsAtom,
} from '@mdekrey/jotai-react-signals';
import { Easing } from '@tweenjs/tween.js';

const sampleMapping = {
	cx: mapAttribute('cx'),
	cy: mapAttribute('cy'),
	r: mapAttribute('r'),
	strokeWidth: mapAttribute('stroke-width'),
} satisfies PartialMapping;

const AnimatedCircle = withSignal('circle', sampleMapping);

type Props = {
	size: number;
};

export default {
	title: 'Example/Jotai Signals',
	argTypes: {
		size: {
			control: {
				type: 'number',
			},
		},
	},
	parameters: {},
} as Meta<Props>;

const Template: Story<Props> = (args) => {
	const store = useStore();
	const size$ = useAsAtom(args.size);
	const tweenedSize$ = useMemo(
		() => tweenedSignal(store, size$, Easing.Quadratic.Out),
		[store, size$]
	);
	const strokeWidth$ = useComputedAtom((get) => get(tweenedSize$) / 15);
	return (
		<svg width="300px" height="300px">
			<AnimatedCircle
				cx={150}
				cy={150}
				r={atom((get) => get(tweenedSize$).toFixed(3))}
				strokeWidth={atom((get) => get(strokeWidth$).toFixed(3))}
				stroke="red"
				fill="none"
			/>
		</svg>
	);
};

export const Primary = Template.bind({});
Primary.args = {
	size: 30,
};
0.3.0

1 year ago

0.4.1

1 year ago

0.4.0

1 year ago

0.2.1

1 year ago

0.2.0

1 year ago

0.1.1

1 year ago

0.1.0

1 year ago