1.0.6 • Published 9 months ago

react-signaler v1.0.6

Weekly downloads
-
License
MIT
Repository
github
Last release
9 months ago

react-signaler

Based on: https://github.com/tc39/proposal-signals

!IMPORTANT! - This is not preact library like this @preact/signals-react. This library is made for react.

Usage

signal\<Type>(value: Type): Signal\<Type>

Signal with atomic value

import { signal } from 'react-signaler';

const num = signal(5);

const Component: FC = () => {
  return <h1>Num: {num()}</h1>;
};

Signal with object value

import { signal } from 'react-signaler';

const num = signal({ num: 5 });

const Component: FC = () => {
  return <h1>Num: {num((value) => value.num)}</h1>;
};

List of signal

import { listOfSignal } from './list-of-signal';

const Component: FC = () => {
  return <div>{listOfSignal.map((signal, idxAsKey) => signal(idxAsKey))}</div>;
};
import { listOfSignal } from './list-of-signal';

const Component: FC = () => {
  return (
    <ul>
      {listOfSignal.map((signal, idxAsKey) =>
        signal((value) => <li>{value}</li>, idxAsKey),
      )}
    </ul>
  );
};

Get Signal value

import { signal } from 'react-signaler';

const num = signal({ num: 5 });
console.log(num.get()); // { num: 5 }
console.log(num.get((value) => value.num)); // 5

Set Signal value

import { signal } from 'react-signaler';

const num = signal({ num: 5 });
num.set({ num: 10 }); // { num: 10 }

Update Signal value

import { signal } from 'react-signaler';

const num = signal({ num: 5 });
num.update((value) => ({ ...value, num: 10 })); // { num: 10 }

Mutate Signal value

import { signal } from 'react-signaler';

const num = signal({ num: 5 });
num.mutate((value) => (value.num = 10)); // { num: 10 }

Use Signal value in react component

import { signal } from 'react-signaler';

const text = signal('');

const Component: FC = () => {
  return (
    <div>
      <h1>text: {text()}</h1>
      {text((value) => (
        <input value={value} onChange={(ev) => text.set(ev.target.value)} />
      ))}
    </div>
  );
};

useSignal() Create Signal in react component

import { useSignal } from 'react-signaler';

const Component: FC = () => {
  const { signal } = useSignal();
  const num = signal('unique-signal-key', () => 5);

  return <h1>Num: {num()}</h1>;
};

computed\<Type>(compute: ()=>Type, watch?: (Signal | ReadonlySignal)[]): ReadonlySignal\<Type>

watch?: (Signal | ReadonlySignal)[] optional argument.In some cases signals are not read in computed but depend on some signals. Watch array is useful for this cases.

Computed values are memoized but, they are always recomputed when the related signals are changed.

Create Computed

import { signal, computed } from 'react-signaler';

const name = signal('Peter');
const upperCaseName = computed(() => name.get().toUpperCase());

const Component: FC = () => {
  return <h1>name: {upperCaseName()}</h1>;
};

List of computed

import { listOfComputed } from './list-of-computed';

const Component: FC = () => {
  return (
    <div>{listOfComputed.map((computed, idxAsKey) => computed(idxAsKey))}</div>
  );
};
import { listOfComputed } from './list-of-computed';

const Component: FC = () => {
  return (
    <ul>
      {listOfComputed.map((computed, idxAsKey) =>
        computed((value) => <li>{value}</li>, idxAsKey),
      )}
    </ul>
  );
};

Create Computed object value

import { signal, computed } from 'react-signaler';

const name = signal('Peter');
const transformedName = computed(() => ({
  upperCase: name.get().toUpperCase(),
  lowerCase: name.get().toLowerCase(),
}));

const Component: FC = () => {
  return <h1>name: {transformedName((value) => value.lowerCase)}</h1>;
};

Read Computed value

import { signal, computed } from 'react-signaler';

const name = signal('Peter');
const transformedName = computed(() => ({
  upperCase: name.get().toUpperCase(),
  lowerCase: name.get().toLowerCase(),
}));

console.log(transformedName.get()); // { upperCase: "PETER", lowerCase: "peter" }
console.log(transformedName.get((value) => value.upperCase)); // PETER

useComputed() Create Computed in react component

import { useSignal, useComputed } from 'react-signaler';

const Component: FC = () => {
  const { signal } = useSignal();
  const { computed } = useComputed();

  const name = signal('name', () => 'Peter');
  const upperCaseName = computed('upperCaseName', () =>
    name.get().toUpperCase(),
  );

  return <h1>name: {upperCaseName()}</h1>;
};

effect(handler: ()=>void, watch?: (Signal | ReadonlySignal)[]): Dispose

watch?: (Signal | ReadonlySignal)[] optional argument.In some cases signals are not read in effect but depend on some signals. Watch array is useful for this cases.

Effects always run once immediately after created.

Effects always run lazy after each related signals are changed.

Crate effect

import { signal, effect } from 'react-signaler';

const name = signal('Peter');

const dispose = effect(() => {
  console.log(name.get());
});

// first console output: Peter

name.set('Erik');

// console output from lazy call: Erik

Dispose effect

Effects are disposables with dispose(...(Signal | ReadonlySignal)[]): void function.Example: dispose();

If the effect is related to multiple signals, there is a possibility to dispose one or more signals.Important if some of the related signals are disposed from the effect but the effect triggered then the disposed signals automatically are subscribing again.Example: dispose(nameSignal, ageSignal);

useSignalEffect() Create Effect in react component

import { useSignal, useComputed, useSignalEffect } from 'react-signaler';

const Component: FC = () => {
  const { signal } = useSignal();
  const { computed } = useComputed();
  const { effect } = useSignalEffect();

  const name = signal('name', () => 'Peter');
  const upperCaseName = computed('upperCaseName', () =>
    name.get().toUpperCase(),
  );

  const dispose = effect('log-user-name', () => {
    console.log(upperCaseName.get());
  });

  return <h1>name: {upperCaseName()}</h1>;
};

lazyEffect(handler: ()=>void, watch: (Signal | ReadonlySignal)[]): Dispose

Lazy effect almost the same as effect.

Lazy effect only runs after each related signals are changed.

Lazy effect watch: (Signal | ReadonlySignal)[] argument is required.

Create Lazy Effect

import { signal, lazyEffect } from 'react-signaler';

const name = signal('Peter');

const dispose = lazyEffect(() => {
  console.log(name.get());
}, [name]);

name.set('Erik');

// first console output from lazy call: Erik

Create Lazy Effect inside react component

import { useSignal, useComputed, useSignalLazyEffect } from 'react-signaler';

const Component: FC = () => {
  const { signal } = useSignal();
  const { computed } = useComputed();
  const { lazyEffect } = useSignalLazyEffect();

  const name = signal('name', () => 'Peter');
  const upperCaseName = computed('upperCaseName', () =>
    name.get().toUpperCase(),
  );

  const dispose = lazyEffect(
    'log-user-name',
    () => {
      console.log(upperCaseName.get());
    },
    [upperCaseName],
  );

  return <h1>name: {upperCaseName()}</h1>;
};

Features

batch\<R>(cb: () => R): R

Batch can avoid unnecessary update steps.

import { signal, effect, batch } from 'react-signaler';

const count = signal(0);

const dispose = effect(() => {
  console.log(count.get());
});

// first console output: 0

count.set(1);
count.set(2);
count.set(3);

// console output: 1
// console output: 2
// console output: 3

batch(() => {
  count.set(1);
  count.set(2);
  count.set(3);
});

// after batch console output: 3

untrack\<R>(signal: Signal\<R>): Runtrack\<R>(signal: ReadonlySignal\<R>): Runtrack\<R>(cb: () => R): R

Untrack can avoid any subscribes.

import { signal, effect } from 'react-signaler';
import { thisFunctionReadOtherSignals } from 'somewhere';

const name = signal('');

const dispose = effect(() => {
  const name = name.get();
  untrack(() => {
    thisFunctionReadOtherSignals(name);
  });
});

name.set('Peter');

Used built-in javascript features