2.1.0 ā€¢ Published 4 years ago

@anissoft/box v2.1.0

Weekly downloads
26
License
ISC
Repository
-
Last release
4 years ago

šŸ“¦ Box

Extremely simple, lightweight and well typed observer for variables in your applications. Just put it in the box!

Installation

Just run npm install command:

$ npm install @anissoft/box --save

Examples

Box with primitive value

import Box from '@anissoft/box';

const nameBox = new Box('Jeremy')

console.log(`Hello, ${nameBox.value}`); // Hello, Jeremy

nameBox.subscribe(
  (newValue, oldValue) => {
    console.log(`Change box value from ${oldValue} to ${newValue}`);
  },
);

nameBox.value = 'Jacob';
console.log(`Hello, ${nameBox.value}`); // Hello, Jacob

nameBox.set('John');
console.log(`Hello, ${nameBox.get()}`); // Hello, Jonh

nameBox.set(oldValue => `${oldValue} Jr`);
console.log(`Hello, ${nameBox.get()}`); // Hello, Jonh Jr

Box with object-like value

import Box from '@anissoft/box';

const objectBox = new Box({ name: 'Mike' });
objectBox.merge({ lastname: 'Wazowski' });

console.log(`${objectBox.pick('name')} - ${objectBox.pick('lastname')}`) // Mike - Wazowski

Box as base class

import Box from '@anissoft/box';

class User extends Box<{
  login: string;
  firstname: string;
  lastname: string;
  preferences?: Record<string, string>
}> {
  constructor(initials: { 
    login: string;
    firstname: string;
    lastname: string;
  }) {
    super(initials);
    ...
  }

  public savePreferences(preferences) {
    this.merge({ preferences })
  }
  ...
}

API

.get()

Return box value

const nameBox = new Box('Jeremy')

nameBox.get(); // Jeremy

.set(newValue)

Set new value in the box and trigger all subcriptions

const nameBox = new Box('Jeremy');
const numberBox = new Box(0);

nameBox.set('Gerry');
numberBox.set(oldValue => oldValue + 1);

nameBox.get(); // Gerry
numberBox.get(); // 1

.subscribe(callback, condition)

Execute callback on every value change. If condition was specified - callback will be executed only if condition returns true;

const numberBox = new Box(0);

numberBox.subscribe(
  () => console.log('value: ',number.box.get());
);

numberBox.subscribe(
  () => console.log('value now greater than 3'),
  (newValue, oldValue) => newValue > 3,
);

setInterval(() => {
  numberBox.set(oldValue => oldValue + 1);
}, 1000);

.merge(newPartialValue)

Like .set, but for object-like values. Instead of replacing an old value, will deeply merge it with the new one

const objectBox = new Box({ foo: 1, bar: 2 });

onjectBox.merge({ bar: 3 });

objectBox.get() // { foo: 1, bar: 3 }

.pick(key)

Returns property from object-like value. Shorthand for box.get()[key]

const objectBox = new Box({ name: 'Finn ', race: 'Human' });

console.log(`${objectBox.pick('name')} the ${objectBox.pick(race)}`);

.update(newPartialValue)

Update works just like .set or .merge, but delay actual value change till the next tick.

const arrayBox = new Box([0,1,2,3,4,5]);

arrayBox.subscribe(() => {
  console.log('I was executed only once');
});

arrayBox.update(value => [...value, 6]);
arrayBox.update(value => [...value, 7]);
arrayBox.update(value => [...value, 8]);
arrayBox.update(value => [...value, 9]);

arrayBox.get(); // [0,1,2,3,4,5]

setTimeout(() => {
  arrayBox.get(); // [0,1,2,3,4,5,9]
}, 0);

You can use the second argument in updater Callback, to access previous update values in current tick:

const arrayBox = new Box([0,1,2,3,4,5]);

arrayBox.subscribe(() => {
  console.log('I was executed only once');
});

arrayBox.update((value) => [...value,  6]);
arrayBox.update((value, canidate) => [...canidate,  7]);
arrayBox.update((value, canidate) => [...canidate,  8]);
arrayBox.update((value, canidate) => [...canidate,  9]);

setTimeout(() => {
  arrayBox.get(); // [0,1,2,3,4,5,6,7,8,9]
}, 0);

With React

You can use boxed value in your React components via handy hook

import * as React from 'react';
import { useBox } from '@anissoft/box';

function Countdown(props: { start: number }) {
  const { 
    get: getRemainingTime, 
    set: setRemainingTime,
    subscribe,
  }  = useBox(props.start);

  React.useEffect(
    () => {
      const interval = setInterval(
        () => setRemainingTime(oldValue => oldValue - 1),
        1000,
      ); 

      const clear = () => clearInterval(interval);
      const unsubscribe = subscribe(
        () => clear(),
        newValue => newValue <= 0,
      );

      return () => {
        clear();
        unsubscribe();
      };
    },
    [],
  );

  return (
    <div>
      <span>Seconds left {getRemainingTime()}</span>
    </div>
  )
}

useBox can accept already created box as agrument:

import * as React from 'react';
import { useBox } from '@anissoft/box';

import myBoxedState from 'Models/state';

function Component() {
  const { get: getState, set: setState } = useBox(myBoxedState);
 
  return (
    <div>
      {Object.entries(getState()).map(
        ([key, value]) => <p>{`${key}: ${value}`}</p>
      )}
    </div>
  )
}

You can pass comparator as second argument, to specify condition when hook should initiate component update:

import * as React from 'react';
import { useBox } from '@anissoft/box';

function Countdown(props: { start: number }) {
  const { 
    get: getRemainingTime, 
    set: setRemainingTime,
  }  = useBox(props.start, (newValue) => newValue >= 0);

  React.useEffect(
    () => {
      const interval = setInterval(
        () => setRemainingTime(oldValue => oldValue - 1),
        1000,
      ); 

      return () => clearInterval(interval);
    },
    [],
  );

  return (
    <div>
      <span>{`Seconds left ${getRemainingTime()}`}</span>
    </div>
  )
}

Author

šŸ‘¤ **Alexey Anisimov

šŸ¤ Contributing

Contributions, issues and feature requests are welcome!

Feel free to check issues page.

2.1.0

4 years ago

2.1.0-beta4

4 years ago

2.1.0-beta3

4 years ago

2.1.0-beta

4 years ago

2.0.3

4 years ago

2.0.2

4 years ago

2.0.1

4 years ago

2.0.0

4 years ago

1.2.3

4 years ago

1.2.2

4 years ago

1.2.1

4 years ago

1.2.0

5 years ago

1.1.10

5 years ago

1.1.9

5 years ago

1.1.8

5 years ago

1.1.2

5 years ago

1.1.0

5 years ago

1.0.3

5 years ago

1.0.2

5 years ago

1.0.1

5 years ago

1.0.0

5 years ago