npm package: @projectnatz/react-signals
Description: Utilize the power of signal state management in your React project!
Author: Francesco Natati
Latest version: 1.0.1

# React Signals
Utilize the power of signal state management in your React project!

## Installation

```bash
npm i react-signals
```

Both **React** and **React Native** are supported.

## Create a Signal

Create a signal from a value or initializer function using `signal()`.

```ts
import { signal } from "react-signals";

const counter = signal(0);

counter.value++;

console.log("Counter value: " + counter.value); // Counter value: 1
```

Every signal is lazy by defualt, so any initializer function passed is only called when the signal value is requested.

```ts
import { signal } from "react-signals";

function expensiveCalculation()
{
	// ...
}

const mySignal = signal(expensiveCalculation); // <- expensiveCalculation is not executed yet

console.log("Signal value: " + mySignal.value); // <- expensiveCalculation is executed and the value returned
console.log("Signal value again: " + mySignal.value); // <- the value previously calculated value is returned
```

You can listen to signal changes and react on them.
A cleanup function is returned after each subscription request.

```ts
import { signal } from "react-signals";

const counter = signal(0);
const unsubscribe = counter.subscribe(() => console.log("Signal value changed to: " + counter.value));

counter.value++; // Signal value changed to: 1
counter.value++; // Signal value changed to: 2

unsubscribe();
counter.value++; // <- no logs are printed because the listener was removed
```

## Create a dependent Signal

Create a new signal from a list of other signal dependencies using `computed()`.
Always remember to cleanup the computed signal after usage.

```ts
import { computed, signal } from "react-signals";

const seconds = signal(150);
const [ minutes, cleanup ] = computed(() => seconds.value / 60, [seconds]);

console.log(minutes.value); // 2.5
seconds.value = 360;
console.log(minutes.value); // 6

cleanup(); // <- cleanup the computed listener when not needed anymore 
```

## Manage side-effects

Run a side-effect immediately and on signal changes using `task()`.
You can return a cleanup function that will run before every side-effect is run.
Always remember to cleanup the task after usage.

```ts
import { signal, task } from "react-signals";

const user = signal("John");
const cleanup = task(
	() =>
	{
		const value = user.value;
		console.log("Connected as user " + value);
		return () => console.log("Disconnected user " + value);
	},
	[user],
); // Connected as user John

user.value = "Paul"; // Disconnected user John
                     // Connected user Paul

cleanup(); // Disconnected user Paul
```

## Manage asynchronous data

Create an asynchronous signal using `resource()`.
You can refetch the data signal and find the current status.

The resource data fetch will start only when `refetch()` is called or the `data` field is accessed.

Accessing the `data` signal's `value` will throw a `Promise` when first loading the data.
This behaviour makes the resource work with `React.Suspence` by default.

Accessing the `data` signal's `value` will re-throw any value thrown by the fetch function.

```ts
import { resource } from "react-signals";

const [ username, cleanup ] = resource(() => Promise.resolve("John"));

console.log(username.status); // pending
username.refetch().then(() => console.log(username.status, username.data.value)); // success John
```

## Includes all the necessary React hooks

### Use a signal value in a component

You can directly show a signal value as a component using `element`.

```tsx
import { signal } from "react-signals";

const counter = signal(0);

function Counter()
{
	return (
		<p>
			Current value: {counter.element}
		</p>

		<button type="button" onClick={() => { counter.value++; }}>increment</button>
	);
}
```

You can retrieve the value in your custom component using `useSignalValue`.
The component will be re-rendered every time the signal changes.

```tsx
import { signal, useSignalValue } from "react-signals";

const counter = signal(0);

function Counter()
{
	const value = useSignalValue(counter);

	return (
		<p>
			Current value: {value}
		</p>

		<button type="button" onClick={() => { counter.value++; }}>increment</button>
	);
}
```

You can also pass a selector to get the specific field you need.
If the retrieved value did not change after a signal update, the component will not re-render.

```tsx
import { signal, useSignalValue } from "react-signals";

const user = signal({ firstName: "John", lastName: "Doe" });

function Foo(props: {})
{
	const firstName = useSignalValue(user, () => user.value.firstName);

	return (
		<p>
			First Name: {firstName}
		</p>
	);
}
```

### Create stable Signal references during render

Create signals, computed signals, tasks or resources using the `useSignal`, `useComputer`, `useTask` and `useResource` hooks during render.

Every reference will be stable and will not change between renders.

```tsx
import { useSignal, useSignalValue } from "react-signals";

function Counter()
{
	const counter = useSignal(0);

	return (
		<p>
			Current value: {counter.element}
		</p>

		<button type="button" onClick={() => { counter.value++; }}>increment</button>
	);
}
```