Options
All
  • Public
  • Public/Protected
  • All
Menu

@nuka/state

Index

Type aliases

AtomUpdater

AtomUpdater<T>: T | AtomUpdateFunction<T>

An atom updater is either a new value or a function that will generate the next value for the atom.

Type parameters

  • T

    The type of the atom's value.

ReadonlyAtomSetter

ReadonlyAtomSetter<T>: (value: T) => void

ReadonlyAtomSetter is the tep of the set function passed to the start function provided to a ReadonlyAtom.

Type parameters

  • T

    The type of the atom's value.

Type declaration

    • (value: T): void
    • Parameters

      • value: T

      Returns void

ReadonlyAtomStarter

ReadonlyAtomStarter<T>: (set: ReadonlyAtomSetter<T>) => VoidFunction | undefined

ReadonlyAtomStarter is a function that initiates the value setting for a ReadonlyAtom, this method is called when the first subscriber to a ReadonlyAtom subscribes and is expected to return a stop function that will cleanup any intervals or timeouts, etc... that the start function created. This stop function is called when the last subscriber unsubscribes from the ReadonlyAtom so that anything that is running is only going to run while being listened to.

It's good practice to always seed with an updated value (if applicable) when the start function is called, for example consider this ReadonlyAtom that represents the current time:

const clock = readonlyAtom(new Date().toLocaleString(), set => {
  set(new Date().toLocaleString());

  const interval = setInterval(() => {
    set(new Date().toLocaleString());
  }, 1000);

  return () => {
    clearInterval(interval);
  };
});

Despite providing an initial value we also set an updated value as the first line in the start function so that after a period of no subscribers we can ensure that we have an updated value instead of a stale value. This does not apply to all use cases though, in some cases there may not be an updated value you can set, perhaps if you're ReadonlyAtom is representing the current user or something that would require a network request for fresh data.

Type parameters

  • T

    The type of the atom's value.

Type declaration

Functions

Const atom

  • atom<T>(value: T): Atom<T>
  • The atom is the core of @nuka/sate. It represents a piece of data that can be set or udpated anywhere in an application and provides the capability for a user to subscribe and listen for the changes. This allows your data to be used in a variety of places around your application while also allowing the consumers that need to respond to changes the capability of knowing when the value has been modified. Examples can range from user authentication tokens, application objects, counts, or any other data where you may need access to it in more than one place but wish to be notified when it's value has changed.

    Example

    const counter = atom(0);
    
    const countDisplay = document.getElementById("display-counter");
    counter.subscribe((atom) => {
      countDisplay.textContent = `Current count: ${atom.value}`;
    });
    
    const incrementButton = document.getElementById("increment-counter");
    incrementButton.addEventListener("click", () => counter.update((n) => n + 1));
    

    See it on CodePen.

    Type parameters

    • T

      The type of the atom's value.

    Parameters

    • value: T

      The initial value of the atom.

    Returns Atom<T>

    A new atom containing the initial value ready for use.

Const projector

  • projector<T, A1>(atom1: IAtom<A1>, projection: (a1: Readonly<A1>) => T): Projector<T>
  • projector<T, A1, A2>(atom1: IAtom<A1>, atom2: IAtom<A2>, projection: (a1: Readonly<A1>, a2: Readonly<A2>) => T): Projector<T>
  • projector<T, A1, A2, A3>(atom1: IAtom<A1>, atom2: IAtom<A2>, atom3: IAtom<A3>, projection: (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>) => T): Projector<T>
  • projector<T, A1, A2, A3, A4>(atom1: IAtom<A1>, atom2: IAtom<A2>, atom3: IAtom<A3>, atom4: IAtom<A4>, projection: (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>) => T): Projector<T>
  • projector<T, A1, A2, A3, A4, A5>(atom1: IAtom<A1>, atom2: IAtom<A2>, atom3: IAtom<A3>, atom4: IAtom<A4>, atom5: IAtom<A5>, projection: (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>) => T): Projector<T>
  • projector<T, A1, A2, A3, A4, A5, A6>(atom1: IAtom<A1>, atom2: IAtom<A2>, atom3: IAtom<A3>, atom4: IAtom<A4>, atom5: IAtom<A5>, atom6: IAtom<A6>, projection: (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>, a6: Readonly<A6>) => T): Projector<T>
  • projector<T, A1, A2, A3, A4, A5, A6, A7>(atom1: IAtom<A1>, atom2: IAtom<A2>, atom3: IAtom<A3>, atom4: IAtom<A4>, atom5: IAtom<A5>, atom6: IAtom<A6>, atom7: IAtom<A7>, projection: (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>, a6: Readonly<A6>, a7: Readonly<A7>) => T): Projector<T>
  • projector<T, A1, A2, A3, A4, A5, A6, A7, A8>(atom1: IAtom<A1>, atom2: IAtom<A2>, atom3: IAtom<A3>, atom4: IAtom<A4>, atom5: IAtom<A5>, atom6: IAtom<A6>, atom7: IAtom<A7>, atom8: IAtom<A8>, projection: (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>, a6: Readonly<A6>, a7: Readonly<A7>, a8: Readonly<A8>) => T): Projector<T>
  • projector<T, A1, A2, A3, A4, A5, A6, A7, A8, A9>(atom1: IAtom<A1>, atom2: IAtom<A2>, atom3: IAtom<A3>, atom4: IAtom<A4>, atom5: IAtom<A5>, atom6: IAtom<A6>, atom7: IAtom<A7>, atom8: IAtom<A8>, atom9: IAtom<A9>, projection: (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>, a6: Readonly<A6>, a7: Readonly<A7>, a8: Readonly<A8>, a9: Readonly<A9>) => T): Projector<T>
  • projector<T, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10>(atom1: IAtom<A1>, atom2: IAtom<A2>, atom3: IAtom<A3>, atom4: IAtom<A4>, atom5: IAtom<A5>, atom6: IAtom<A6>, atom7: IAtom<A7>, atom8: IAtom<A8>, atom9: IAtom<A9>, atom10: IAtom<A10>, projection: (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>, a6: Readonly<A6>, a7: Readonly<A7>, a8: Readonly<A8>, a9: Readonly<A9>, a10: Readonly<A10>) => T): Projector<T>
  • projector<T, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11>(atom1: IAtom<A1>, atom2: IAtom<A2>, atom3: IAtom<A3>, atom4: IAtom<A4>, atom5: IAtom<A5>, atom6: IAtom<A6>, atom7: IAtom<A7>, atom8: IAtom<A8>, atom9: IAtom<A9>, atom10: IAtom<A10>, atom11: IAtom<A11>, projection: (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>, a6: Readonly<A6>, a7: Readonly<A7>, a8: Readonly<A8>, a9: Readonly<A9>, a10: Readonly<A10>, a11: Readonly<A11>) => T): Projector<T>
  • projector<T, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12>(atom1: IAtom<A1>, atom2: IAtom<A2>, atom3: IAtom<A3>, atom4: IAtom<A4>, atom5: IAtom<A5>, atom6: IAtom<A6>, atom7: IAtom<A7>, atom8: IAtom<A8>, atom9: IAtom<A9>, atom10: IAtom<A10>, atom11: IAtom<A11>, atom12: IAtom<A12>, projection: (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>, a6: Readonly<A6>, a7: Readonly<A7>, a8: Readonly<A8>, a9: Readonly<A9>, a10: Readonly<A10>, a11: Readonly<A11>, a12: Readonly<A12>) => T): Projector<T>
  • Create a new projector, which will take in atom-like objects as input and a function that combines their values into a new value. Commonly this is referred to as a "computed value." Projectors can be used to many purposes, if the new data is just a unique combination of existing values then it's better to use a Projector which will automatically subscribe to the passed in atom-like objects and update when they update. Coordinating this without a projector is possible but why go through all that extra work?

    NOTE

    Since ReadonlyAtom's don't "start" when they don't have subscribers, Projector tries to play smart and mimic this. If it detects that it received a ReadonlyAtom as an input atom it will not subscribe to it unless the Projector itself has a subscriber. If the ReadonlyAtom is live and running then the Projector will be out of sync with it's current value. To fix this, provide a no-op subscriber to the projector or subscribe to it to receive reactive updates.

    Examples

    Probably the simpleist example of a projector is summing up multiple count atoms.

    const counter1 = atom(0);
    const counter2 = atom(0);
    
    const display1 = document.getElementById("display-counter-1");
    counter1.subscribe((count) => {
      display1.textContent = `Current count: ${count.value}`;
    });
    
    const incr1 = document.getElementById("increment-counter-1");
    incr1.addEventListener("click", () => counter1.update((n) => n + 1));
    
    const display2 = document.getElementById("display-counter-2");
    counter2.subscribe((count) => {
      display2.textContent = `Current count: ${count.value}`;
    });
    
    const incr2 = document.getElementById("increment-counter-2");
    incr2.addEventListener("click", () => counter2.update((n) => n + 1));
    
    const sum = projector(counter1, counter2, (a, b) => a + b);
    
    const sumDisplay = document.getElementById("display-sum");
    sum.subscribe((sum) => {
      sumDisplay.textContent = `Total count: ${sum.value}`;
    });
    

    See it on CodePen.

    Projectors don't always have to be used with multiple atoms, somtimes you just want to be able to have and update data from a single atom in a different way. Like showing the full name for a user.

    const user = atom({
      firstName: 'Peter',
      lastName: 'Programmer',
    });
    
    const fullName = projector(user, u => `${u.firstName} ${u.lastName}`);
    
    const firstNameInput = document.getElementById('first-name');
    firstNameInput.value = user.value.firstName;
    firstNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, firstName: event.target.value }));
    });
    
    const lastNameInput = document.getElementById('last-name');
    lastNameInput.value = user.value.lastName;
    lastNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, lastName: event.target.value }));
    });
    
    const fullNameDisplay = document.getElementById('display-full-name');
    fullNameDisplay.textContent = fullName.value;
    fullName.subscribe(name => {
      fullNameDisplay.textContent = name.value;
    });
    

    See it on CodePen

    A simple and clean way to control how data is rendered, just modify fullName's projection function to, say, change the computed value to last, first instead.

    Type parameters

    • T

    • A1

    Parameters

    • atom1: IAtom<A1>
    • projection: (a1: Readonly<A1>) => T
        • (a1: Readonly<A1>): T
        • Parameters

          • a1: Readonly<A1>

          Returns T

    Returns Projector<T>

    A new projector that will act like an atom (has a value, subscribe, and unsubscribe function). This projector's projection function will be executed when any of the atoms provided are updated.

  • Create a new projector, which will take in atom-like objects as input and a function that combines their values into a new value. Commonly this is referred to as a "computed value." Projectors can be used to many purposes, if the new data is just a unique combination of existing values then it's better to use a Projector which will automatically subscribe to the passed in atom-like objects and update when they update. Coordinating this without a projector is possible but why go through all that extra work?

    NOTE

    Since ReadonlyAtom's don't "start" when they don't have subscribers, Projector tries to play smart and mimic this. If it detects that it received a ReadonlyAtom as an input atom it will not subscribe to it unless the Projector itself has a subscriber. If the ReadonlyAtom is live and running then the Projector will be out of sync with it's current value. To fix this, provide a no-op subscriber to the projector or subscribe to it to receive reactive updates.

    Examples

    Probably the simpleist example of a projector is summing up multiple count atoms.

    const counter1 = atom(0);
    const counter2 = atom(0);
    
    const display1 = document.getElementById("display-counter-1");
    counter1.subscribe((count) => {
      display1.textContent = `Current count: ${count.value}`;
    });
    
    const incr1 = document.getElementById("increment-counter-1");
    incr1.addEventListener("click", () => counter1.update((n) => n + 1));
    
    const display2 = document.getElementById("display-counter-2");
    counter2.subscribe((count) => {
      display2.textContent = `Current count: ${count.value}`;
    });
    
    const incr2 = document.getElementById("increment-counter-2");
    incr2.addEventListener("click", () => counter2.update((n) => n + 1));
    
    const sum = projector(counter1, counter2, (a, b) => a + b);
    
    const sumDisplay = document.getElementById("display-sum");
    sum.subscribe((sum) => {
      sumDisplay.textContent = `Total count: ${sum.value}`;
    });
    

    See it on CodePen.

    Projectors don't always have to be used with multiple atoms, somtimes you just want to be able to have and update data from a single atom in a different way. Like showing the full name for a user.

    const user = atom({
      firstName: 'Peter',
      lastName: 'Programmer',
    });
    
    const fullName = projector(user, u => `${u.firstName} ${u.lastName}`);
    
    const firstNameInput = document.getElementById('first-name');
    firstNameInput.value = user.value.firstName;
    firstNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, firstName: event.target.value }));
    });
    
    const lastNameInput = document.getElementById('last-name');
    lastNameInput.value = user.value.lastName;
    lastNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, lastName: event.target.value }));
    });
    
    const fullNameDisplay = document.getElementById('display-full-name');
    fullNameDisplay.textContent = fullName.value;
    fullName.subscribe(name => {
      fullNameDisplay.textContent = name.value;
    });
    

    See it on CodePen

    A simple and clean way to control how data is rendered, just modify fullName's projection function to, say, change the computed value to last, first instead.

    Type parameters

    • T

    • A1

    • A2

    Parameters

    • atom1: IAtom<A1>
    • atom2: IAtom<A2>
    • projection: (a1: Readonly<A1>, a2: Readonly<A2>) => T
        • (a1: Readonly<A1>, a2: Readonly<A2>): T
        • Parameters

          • a1: Readonly<A1>
          • a2: Readonly<A2>

          Returns T

    Returns Projector<T>

    A new projector that will act like an atom (has a value, subscribe, and unsubscribe function). This projector's projection function will be executed when any of the atoms provided are updated.

  • Create a new projector, which will take in atom-like objects as input and a function that combines their values into a new value. Commonly this is referred to as a "computed value." Projectors can be used to many purposes, if the new data is just a unique combination of existing values then it's better to use a Projector which will automatically subscribe to the passed in atom-like objects and update when they update. Coordinating this without a projector is possible but why go through all that extra work?

    NOTE

    Since ReadonlyAtom's don't "start" when they don't have subscribers, Projector tries to play smart and mimic this. If it detects that it received a ReadonlyAtom as an input atom it will not subscribe to it unless the Projector itself has a subscriber. If the ReadonlyAtom is live and running then the Projector will be out of sync with it's current value. To fix this, provide a no-op subscriber to the projector or subscribe to it to receive reactive updates.

    Examples

    Probably the simpleist example of a projector is summing up multiple count atoms.

    const counter1 = atom(0);
    const counter2 = atom(0);
    
    const display1 = document.getElementById("display-counter-1");
    counter1.subscribe((count) => {
      display1.textContent = `Current count: ${count.value}`;
    });
    
    const incr1 = document.getElementById("increment-counter-1");
    incr1.addEventListener("click", () => counter1.update((n) => n + 1));
    
    const display2 = document.getElementById("display-counter-2");
    counter2.subscribe((count) => {
      display2.textContent = `Current count: ${count.value}`;
    });
    
    const incr2 = document.getElementById("increment-counter-2");
    incr2.addEventListener("click", () => counter2.update((n) => n + 1));
    
    const sum = projector(counter1, counter2, (a, b) => a + b);
    
    const sumDisplay = document.getElementById("display-sum");
    sum.subscribe((sum) => {
      sumDisplay.textContent = `Total count: ${sum.value}`;
    });
    

    See it on CodePen.

    Projectors don't always have to be used with multiple atoms, somtimes you just want to be able to have and update data from a single atom in a different way. Like showing the full name for a user.

    const user = atom({
      firstName: 'Peter',
      lastName: 'Programmer',
    });
    
    const fullName = projector(user, u => `${u.firstName} ${u.lastName}`);
    
    const firstNameInput = document.getElementById('first-name');
    firstNameInput.value = user.value.firstName;
    firstNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, firstName: event.target.value }));
    });
    
    const lastNameInput = document.getElementById('last-name');
    lastNameInput.value = user.value.lastName;
    lastNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, lastName: event.target.value }));
    });
    
    const fullNameDisplay = document.getElementById('display-full-name');
    fullNameDisplay.textContent = fullName.value;
    fullName.subscribe(name => {
      fullNameDisplay.textContent = name.value;
    });
    

    See it on CodePen

    A simple and clean way to control how data is rendered, just modify fullName's projection function to, say, change the computed value to last, first instead.

    Type parameters

    • T

    • A1

    • A2

    • A3

    Parameters

    • atom1: IAtom<A1>
    • atom2: IAtom<A2>
    • atom3: IAtom<A3>
    • projection: (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>) => T
        • (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>): T
        • Parameters

          • a1: Readonly<A1>
          • a2: Readonly<A2>
          • a3: Readonly<A3>

          Returns T

    Returns Projector<T>

    A new projector that will act like an atom (has a value, subscribe, and unsubscribe function). This projector's projection function will be executed when any of the atoms provided are updated.

  • Create a new projector, which will take in atom-like objects as input and a function that combines their values into a new value. Commonly this is referred to as a "computed value." Projectors can be used to many purposes, if the new data is just a unique combination of existing values then it's better to use a Projector which will automatically subscribe to the passed in atom-like objects and update when they update. Coordinating this without a projector is possible but why go through all that extra work?

    NOTE

    Since ReadonlyAtom's don't "start" when they don't have subscribers, Projector tries to play smart and mimic this. If it detects that it received a ReadonlyAtom as an input atom it will not subscribe to it unless the Projector itself has a subscriber. If the ReadonlyAtom is live and running then the Projector will be out of sync with it's current value. To fix this, provide a no-op subscriber to the projector or subscribe to it to receive reactive updates.

    Examples

    Probably the simpleist example of a projector is summing up multiple count atoms.

    const counter1 = atom(0);
    const counter2 = atom(0);
    
    const display1 = document.getElementById("display-counter-1");
    counter1.subscribe((count) => {
      display1.textContent = `Current count: ${count.value}`;
    });
    
    const incr1 = document.getElementById("increment-counter-1");
    incr1.addEventListener("click", () => counter1.update((n) => n + 1));
    
    const display2 = document.getElementById("display-counter-2");
    counter2.subscribe((count) => {
      display2.textContent = `Current count: ${count.value}`;
    });
    
    const incr2 = document.getElementById("increment-counter-2");
    incr2.addEventListener("click", () => counter2.update((n) => n + 1));
    
    const sum = projector(counter1, counter2, (a, b) => a + b);
    
    const sumDisplay = document.getElementById("display-sum");
    sum.subscribe((sum) => {
      sumDisplay.textContent = `Total count: ${sum.value}`;
    });
    

    See it on CodePen.

    Projectors don't always have to be used with multiple atoms, somtimes you just want to be able to have and update data from a single atom in a different way. Like showing the full name for a user.

    const user = atom({
      firstName: 'Peter',
      lastName: 'Programmer',
    });
    
    const fullName = projector(user, u => `${u.firstName} ${u.lastName}`);
    
    const firstNameInput = document.getElementById('first-name');
    firstNameInput.value = user.value.firstName;
    firstNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, firstName: event.target.value }));
    });
    
    const lastNameInput = document.getElementById('last-name');
    lastNameInput.value = user.value.lastName;
    lastNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, lastName: event.target.value }));
    });
    
    const fullNameDisplay = document.getElementById('display-full-name');
    fullNameDisplay.textContent = fullName.value;
    fullName.subscribe(name => {
      fullNameDisplay.textContent = name.value;
    });
    

    See it on CodePen

    A simple and clean way to control how data is rendered, just modify fullName's projection function to, say, change the computed value to last, first instead.

    Type parameters

    • T

    • A1

    • A2

    • A3

    • A4

    Parameters

    • atom1: IAtom<A1>
    • atom2: IAtom<A2>
    • atom3: IAtom<A3>
    • atom4: IAtom<A4>
    • projection: (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>) => T
        • (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>): T
        • Parameters

          • a1: Readonly<A1>
          • a2: Readonly<A2>
          • a3: Readonly<A3>
          • a4: Readonly<A4>

          Returns T

    Returns Projector<T>

    A new projector that will act like an atom (has a value, subscribe, and unsubscribe function). This projector's projection function will be executed when any of the atoms provided are updated.

  • Create a new projector, which will take in atom-like objects as input and a function that combines their values into a new value. Commonly this is referred to as a "computed value." Projectors can be used to many purposes, if the new data is just a unique combination of existing values then it's better to use a Projector which will automatically subscribe to the passed in atom-like objects and update when they update. Coordinating this without a projector is possible but why go through all that extra work?

    NOTE

    Since ReadonlyAtom's don't "start" when they don't have subscribers, Projector tries to play smart and mimic this. If it detects that it received a ReadonlyAtom as an input atom it will not subscribe to it unless the Projector itself has a subscriber. If the ReadonlyAtom is live and running then the Projector will be out of sync with it's current value. To fix this, provide a no-op subscriber to the projector or subscribe to it to receive reactive updates.

    Examples

    Probably the simpleist example of a projector is summing up multiple count atoms.

    const counter1 = atom(0);
    const counter2 = atom(0);
    
    const display1 = document.getElementById("display-counter-1");
    counter1.subscribe((count) => {
      display1.textContent = `Current count: ${count.value}`;
    });
    
    const incr1 = document.getElementById("increment-counter-1");
    incr1.addEventListener("click", () => counter1.update((n) => n + 1));
    
    const display2 = document.getElementById("display-counter-2");
    counter2.subscribe((count) => {
      display2.textContent = `Current count: ${count.value}`;
    });
    
    const incr2 = document.getElementById("increment-counter-2");
    incr2.addEventListener("click", () => counter2.update((n) => n + 1));
    
    const sum = projector(counter1, counter2, (a, b) => a + b);
    
    const sumDisplay = document.getElementById("display-sum");
    sum.subscribe((sum) => {
      sumDisplay.textContent = `Total count: ${sum.value}`;
    });
    

    See it on CodePen.

    Projectors don't always have to be used with multiple atoms, somtimes you just want to be able to have and update data from a single atom in a different way. Like showing the full name for a user.

    const user = atom({
      firstName: 'Peter',
      lastName: 'Programmer',
    });
    
    const fullName = projector(user, u => `${u.firstName} ${u.lastName}`);
    
    const firstNameInput = document.getElementById('first-name');
    firstNameInput.value = user.value.firstName;
    firstNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, firstName: event.target.value }));
    });
    
    const lastNameInput = document.getElementById('last-name');
    lastNameInput.value = user.value.lastName;
    lastNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, lastName: event.target.value }));
    });
    
    const fullNameDisplay = document.getElementById('display-full-name');
    fullNameDisplay.textContent = fullName.value;
    fullName.subscribe(name => {
      fullNameDisplay.textContent = name.value;
    });
    

    See it on CodePen

    A simple and clean way to control how data is rendered, just modify fullName's projection function to, say, change the computed value to last, first instead.

    Type parameters

    • T

    • A1

    • A2

    • A3

    • A4

    • A5

    Parameters

    • atom1: IAtom<A1>
    • atom2: IAtom<A2>
    • atom3: IAtom<A3>
    • atom4: IAtom<A4>
    • atom5: IAtom<A5>
    • projection: (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>) => T
        • (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>): T
        • Parameters

          • a1: Readonly<A1>
          • a2: Readonly<A2>
          • a3: Readonly<A3>
          • a4: Readonly<A4>
          • a5: Readonly<A5>

          Returns T

    Returns Projector<T>

    A new projector that will act like an atom (has a value, subscribe, and unsubscribe function). This projector's projection function will be executed when any of the atoms provided are updated.

  • Create a new projector, which will take in atom-like objects as input and a function that combines their values into a new value. Commonly this is referred to as a "computed value." Projectors can be used to many purposes, if the new data is just a unique combination of existing values then it's better to use a Projector which will automatically subscribe to the passed in atom-like objects and update when they update. Coordinating this without a projector is possible but why go through all that extra work?

    NOTE

    Since ReadonlyAtom's don't "start" when they don't have subscribers, Projector tries to play smart and mimic this. If it detects that it received a ReadonlyAtom as an input atom it will not subscribe to it unless the Projector itself has a subscriber. If the ReadonlyAtom is live and running then the Projector will be out of sync with it's current value. To fix this, provide a no-op subscriber to the projector or subscribe to it to receive reactive updates.

    Examples

    Probably the simpleist example of a projector is summing up multiple count atoms.

    const counter1 = atom(0);
    const counter2 = atom(0);
    
    const display1 = document.getElementById("display-counter-1");
    counter1.subscribe((count) => {
      display1.textContent = `Current count: ${count.value}`;
    });
    
    const incr1 = document.getElementById("increment-counter-1");
    incr1.addEventListener("click", () => counter1.update((n) => n + 1));
    
    const display2 = document.getElementById("display-counter-2");
    counter2.subscribe((count) => {
      display2.textContent = `Current count: ${count.value}`;
    });
    
    const incr2 = document.getElementById("increment-counter-2");
    incr2.addEventListener("click", () => counter2.update((n) => n + 1));
    
    const sum = projector(counter1, counter2, (a, b) => a + b);
    
    const sumDisplay = document.getElementById("display-sum");
    sum.subscribe((sum) => {
      sumDisplay.textContent = `Total count: ${sum.value}`;
    });
    

    See it on CodePen.

    Projectors don't always have to be used with multiple atoms, somtimes you just want to be able to have and update data from a single atom in a different way. Like showing the full name for a user.

    const user = atom({
      firstName: 'Peter',
      lastName: 'Programmer',
    });
    
    const fullName = projector(user, u => `${u.firstName} ${u.lastName}`);
    
    const firstNameInput = document.getElementById('first-name');
    firstNameInput.value = user.value.firstName;
    firstNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, firstName: event.target.value }));
    });
    
    const lastNameInput = document.getElementById('last-name');
    lastNameInput.value = user.value.lastName;
    lastNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, lastName: event.target.value }));
    });
    
    const fullNameDisplay = document.getElementById('display-full-name');
    fullNameDisplay.textContent = fullName.value;
    fullName.subscribe(name => {
      fullNameDisplay.textContent = name.value;
    });
    

    See it on CodePen

    A simple and clean way to control how data is rendered, just modify fullName's projection function to, say, change the computed value to last, first instead.

    Type parameters

    • T

    • A1

    • A2

    • A3

    • A4

    • A5

    • A6

    Parameters

    • atom1: IAtom<A1>
    • atom2: IAtom<A2>
    • atom3: IAtom<A3>
    • atom4: IAtom<A4>
    • atom5: IAtom<A5>
    • atom6: IAtom<A6>
    • projection: (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>, a6: Readonly<A6>) => T
        • (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>, a6: Readonly<A6>): T
        • Parameters

          • a1: Readonly<A1>
          • a2: Readonly<A2>
          • a3: Readonly<A3>
          • a4: Readonly<A4>
          • a5: Readonly<A5>
          • a6: Readonly<A6>

          Returns T

    Returns Projector<T>

    A new projector that will act like an atom (has a value, subscribe, and unsubscribe function). This projector's projection function will be executed when any of the atoms provided are updated.

  • Create a new projector, which will take in atom-like objects as input and a function that combines their values into a new value. Commonly this is referred to as a "computed value." Projectors can be used to many purposes, if the new data is just a unique combination of existing values then it's better to use a Projector which will automatically subscribe to the passed in atom-like objects and update when they update. Coordinating this without a projector is possible but why go through all that extra work?

    NOTE

    Since ReadonlyAtom's don't "start" when they don't have subscribers, Projector tries to play smart and mimic this. If it detects that it received a ReadonlyAtom as an input atom it will not subscribe to it unless the Projector itself has a subscriber. If the ReadonlyAtom is live and running then the Projector will be out of sync with it's current value. To fix this, provide a no-op subscriber to the projector or subscribe to it to receive reactive updates.

    Examples

    Probably the simpleist example of a projector is summing up multiple count atoms.

    const counter1 = atom(0);
    const counter2 = atom(0);
    
    const display1 = document.getElementById("display-counter-1");
    counter1.subscribe((count) => {
      display1.textContent = `Current count: ${count.value}`;
    });
    
    const incr1 = document.getElementById("increment-counter-1");
    incr1.addEventListener("click", () => counter1.update((n) => n + 1));
    
    const display2 = document.getElementById("display-counter-2");
    counter2.subscribe((count) => {
      display2.textContent = `Current count: ${count.value}`;
    });
    
    const incr2 = document.getElementById("increment-counter-2");
    incr2.addEventListener("click", () => counter2.update((n) => n + 1));
    
    const sum = projector(counter1, counter2, (a, b) => a + b);
    
    const sumDisplay = document.getElementById("display-sum");
    sum.subscribe((sum) => {
      sumDisplay.textContent = `Total count: ${sum.value}`;
    });
    

    See it on CodePen.

    Projectors don't always have to be used with multiple atoms, somtimes you just want to be able to have and update data from a single atom in a different way. Like showing the full name for a user.

    const user = atom({
      firstName: 'Peter',
      lastName: 'Programmer',
    });
    
    const fullName = projector(user, u => `${u.firstName} ${u.lastName}`);
    
    const firstNameInput = document.getElementById('first-name');
    firstNameInput.value = user.value.firstName;
    firstNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, firstName: event.target.value }));
    });
    
    const lastNameInput = document.getElementById('last-name');
    lastNameInput.value = user.value.lastName;
    lastNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, lastName: event.target.value }));
    });
    
    const fullNameDisplay = document.getElementById('display-full-name');
    fullNameDisplay.textContent = fullName.value;
    fullName.subscribe(name => {
      fullNameDisplay.textContent = name.value;
    });
    

    See it on CodePen

    A simple and clean way to control how data is rendered, just modify fullName's projection function to, say, change the computed value to last, first instead.

    Type parameters

    • T

    • A1

    • A2

    • A3

    • A4

    • A5

    • A6

    • A7

    Parameters

    • atom1: IAtom<A1>
    • atom2: IAtom<A2>
    • atom3: IAtom<A3>
    • atom4: IAtom<A4>
    • atom5: IAtom<A5>
    • atom6: IAtom<A6>
    • atom7: IAtom<A7>
    • projection: (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>, a6: Readonly<A6>, a7: Readonly<A7>) => T
        • (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>, a6: Readonly<A6>, a7: Readonly<A7>): T
        • Parameters

          • a1: Readonly<A1>
          • a2: Readonly<A2>
          • a3: Readonly<A3>
          • a4: Readonly<A4>
          • a5: Readonly<A5>
          • a6: Readonly<A6>
          • a7: Readonly<A7>

          Returns T

    Returns Projector<T>

    A new projector that will act like an atom (has a value, subscribe, and unsubscribe function). This projector's projection function will be executed when any of the atoms provided are updated.

  • Create a new projector, which will take in atom-like objects as input and a function that combines their values into a new value. Commonly this is referred to as a "computed value." Projectors can be used to many purposes, if the new data is just a unique combination of existing values then it's better to use a Projector which will automatically subscribe to the passed in atom-like objects and update when they update. Coordinating this without a projector is possible but why go through all that extra work?

    NOTE

    Since ReadonlyAtom's don't "start" when they don't have subscribers, Projector tries to play smart and mimic this. If it detects that it received a ReadonlyAtom as an input atom it will not subscribe to it unless the Projector itself has a subscriber. If the ReadonlyAtom is live and running then the Projector will be out of sync with it's current value. To fix this, provide a no-op subscriber to the projector or subscribe to it to receive reactive updates.

    Examples

    Probably the simpleist example of a projector is summing up multiple count atoms.

    const counter1 = atom(0);
    const counter2 = atom(0);
    
    const display1 = document.getElementById("display-counter-1");
    counter1.subscribe((count) => {
      display1.textContent = `Current count: ${count.value}`;
    });
    
    const incr1 = document.getElementById("increment-counter-1");
    incr1.addEventListener("click", () => counter1.update((n) => n + 1));
    
    const display2 = document.getElementById("display-counter-2");
    counter2.subscribe((count) => {
      display2.textContent = `Current count: ${count.value}`;
    });
    
    const incr2 = document.getElementById("increment-counter-2");
    incr2.addEventListener("click", () => counter2.update((n) => n + 1));
    
    const sum = projector(counter1, counter2, (a, b) => a + b);
    
    const sumDisplay = document.getElementById("display-sum");
    sum.subscribe((sum) => {
      sumDisplay.textContent = `Total count: ${sum.value}`;
    });
    

    See it on CodePen.

    Projectors don't always have to be used with multiple atoms, somtimes you just want to be able to have and update data from a single atom in a different way. Like showing the full name for a user.

    const user = atom({
      firstName: 'Peter',
      lastName: 'Programmer',
    });
    
    const fullName = projector(user, u => `${u.firstName} ${u.lastName}`);
    
    const firstNameInput = document.getElementById('first-name');
    firstNameInput.value = user.value.firstName;
    firstNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, firstName: event.target.value }));
    });
    
    const lastNameInput = document.getElementById('last-name');
    lastNameInput.value = user.value.lastName;
    lastNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, lastName: event.target.value }));
    });
    
    const fullNameDisplay = document.getElementById('display-full-name');
    fullNameDisplay.textContent = fullName.value;
    fullName.subscribe(name => {
      fullNameDisplay.textContent = name.value;
    });
    

    See it on CodePen

    A simple and clean way to control how data is rendered, just modify fullName's projection function to, say, change the computed value to last, first instead.

    Type parameters

    • T

    • A1

    • A2

    • A3

    • A4

    • A5

    • A6

    • A7

    • A8

    Parameters

    • atom1: IAtom<A1>
    • atom2: IAtom<A2>
    • atom3: IAtom<A3>
    • atom4: IAtom<A4>
    • atom5: IAtom<A5>
    • atom6: IAtom<A6>
    • atom7: IAtom<A7>
    • atom8: IAtom<A8>
    • projection: (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>, a6: Readonly<A6>, a7: Readonly<A7>, a8: Readonly<A8>) => T
        • (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>, a6: Readonly<A6>, a7: Readonly<A7>, a8: Readonly<A8>): T
        • Parameters

          • a1: Readonly<A1>
          • a2: Readonly<A2>
          • a3: Readonly<A3>
          • a4: Readonly<A4>
          • a5: Readonly<A5>
          • a6: Readonly<A6>
          • a7: Readonly<A7>
          • a8: Readonly<A8>

          Returns T

    Returns Projector<T>

    A new projector that will act like an atom (has a value, subscribe, and unsubscribe function). This projector's projection function will be executed when any of the atoms provided are updated.

  • Create a new projector, which will take in atom-like objects as input and a function that combines their values into a new value. Commonly this is referred to as a "computed value." Projectors can be used to many purposes, if the new data is just a unique combination of existing values then it's better to use a Projector which will automatically subscribe to the passed in atom-like objects and update when they update. Coordinating this without a projector is possible but why go through all that extra work?

    NOTE

    Since ReadonlyAtom's don't "start" when they don't have subscribers, Projector tries to play smart and mimic this. If it detects that it received a ReadonlyAtom as an input atom it will not subscribe to it unless the Projector itself has a subscriber. If the ReadonlyAtom is live and running then the Projector will be out of sync with it's current value. To fix this, provide a no-op subscriber to the projector or subscribe to it to receive reactive updates.

    Examples

    Probably the simpleist example of a projector is summing up multiple count atoms.

    const counter1 = atom(0);
    const counter2 = atom(0);
    
    const display1 = document.getElementById("display-counter-1");
    counter1.subscribe((count) => {
      display1.textContent = `Current count: ${count.value}`;
    });
    
    const incr1 = document.getElementById("increment-counter-1");
    incr1.addEventListener("click", () => counter1.update((n) => n + 1));
    
    const display2 = document.getElementById("display-counter-2");
    counter2.subscribe((count) => {
      display2.textContent = `Current count: ${count.value}`;
    });
    
    const incr2 = document.getElementById("increment-counter-2");
    incr2.addEventListener("click", () => counter2.update((n) => n + 1));
    
    const sum = projector(counter1, counter2, (a, b) => a + b);
    
    const sumDisplay = document.getElementById("display-sum");
    sum.subscribe((sum) => {
      sumDisplay.textContent = `Total count: ${sum.value}`;
    });
    

    See it on CodePen.

    Projectors don't always have to be used with multiple atoms, somtimes you just want to be able to have and update data from a single atom in a different way. Like showing the full name for a user.

    const user = atom({
      firstName: 'Peter',
      lastName: 'Programmer',
    });
    
    const fullName = projector(user, u => `${u.firstName} ${u.lastName}`);
    
    const firstNameInput = document.getElementById('first-name');
    firstNameInput.value = user.value.firstName;
    firstNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, firstName: event.target.value }));
    });
    
    const lastNameInput = document.getElementById('last-name');
    lastNameInput.value = user.value.lastName;
    lastNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, lastName: event.target.value }));
    });
    
    const fullNameDisplay = document.getElementById('display-full-name');
    fullNameDisplay.textContent = fullName.value;
    fullName.subscribe(name => {
      fullNameDisplay.textContent = name.value;
    });
    

    See it on CodePen

    A simple and clean way to control how data is rendered, just modify fullName's projection function to, say, change the computed value to last, first instead.

    Type parameters

    • T

    • A1

    • A2

    • A3

    • A4

    • A5

    • A6

    • A7

    • A8

    • A9

    Parameters

    • atom1: IAtom<A1>
    • atom2: IAtom<A2>
    • atom3: IAtom<A3>
    • atom4: IAtom<A4>
    • atom5: IAtom<A5>
    • atom6: IAtom<A6>
    • atom7: IAtom<A7>
    • atom8: IAtom<A8>
    • atom9: IAtom<A9>
    • projection: (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>, a6: Readonly<A6>, a7: Readonly<A7>, a8: Readonly<A8>, a9: Readonly<A9>) => T
        • (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>, a6: Readonly<A6>, a7: Readonly<A7>, a8: Readonly<A8>, a9: Readonly<A9>): T
        • Parameters

          • a1: Readonly<A1>
          • a2: Readonly<A2>
          • a3: Readonly<A3>
          • a4: Readonly<A4>
          • a5: Readonly<A5>
          • a6: Readonly<A6>
          • a7: Readonly<A7>
          • a8: Readonly<A8>
          • a9: Readonly<A9>

          Returns T

    Returns Projector<T>

    A new projector that will act like an atom (has a value, subscribe, and unsubscribe function). This projector's projection function will be executed when any of the atoms provided are updated.

  • Create a new projector, which will take in atom-like objects as input and a function that combines their values into a new value. Commonly this is referred to as a "computed value." Projectors can be used to many purposes, if the new data is just a unique combination of existing values then it's better to use a Projector which will automatically subscribe to the passed in atom-like objects and update when they update. Coordinating this without a projector is possible but why go through all that extra work?

    NOTE

    Since ReadonlyAtom's don't "start" when they don't have subscribers, Projector tries to play smart and mimic this. If it detects that it received a ReadonlyAtom as an input atom it will not subscribe to it unless the Projector itself has a subscriber. If the ReadonlyAtom is live and running then the Projector will be out of sync with it's current value. To fix this, provide a no-op subscriber to the projector or subscribe to it to receive reactive updates.

    Examples

    Probably the simpleist example of a projector is summing up multiple count atoms.

    const counter1 = atom(0);
    const counter2 = atom(0);
    
    const display1 = document.getElementById("display-counter-1");
    counter1.subscribe((count) => {
      display1.textContent = `Current count: ${count.value}`;
    });
    
    const incr1 = document.getElementById("increment-counter-1");
    incr1.addEventListener("click", () => counter1.update((n) => n + 1));
    
    const display2 = document.getElementById("display-counter-2");
    counter2.subscribe((count) => {
      display2.textContent = `Current count: ${count.value}`;
    });
    
    const incr2 = document.getElementById("increment-counter-2");
    incr2.addEventListener("click", () => counter2.update((n) => n + 1));
    
    const sum = projector(counter1, counter2, (a, b) => a + b);
    
    const sumDisplay = document.getElementById("display-sum");
    sum.subscribe((sum) => {
      sumDisplay.textContent = `Total count: ${sum.value}`;
    });
    

    See it on CodePen.

    Projectors don't always have to be used with multiple atoms, somtimes you just want to be able to have and update data from a single atom in a different way. Like showing the full name for a user.

    const user = atom({
      firstName: 'Peter',
      lastName: 'Programmer',
    });
    
    const fullName = projector(user, u => `${u.firstName} ${u.lastName}`);
    
    const firstNameInput = document.getElementById('first-name');
    firstNameInput.value = user.value.firstName;
    firstNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, firstName: event.target.value }));
    });
    
    const lastNameInput = document.getElementById('last-name');
    lastNameInput.value = user.value.lastName;
    lastNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, lastName: event.target.value }));
    });
    
    const fullNameDisplay = document.getElementById('display-full-name');
    fullNameDisplay.textContent = fullName.value;
    fullName.subscribe(name => {
      fullNameDisplay.textContent = name.value;
    });
    

    See it on CodePen

    A simple and clean way to control how data is rendered, just modify fullName's projection function to, say, change the computed value to last, first instead.

    Type parameters

    • T

    • A1

    • A2

    • A3

    • A4

    • A5

    • A6

    • A7

    • A8

    • A9

    • A10

    Parameters

    • atom1: IAtom<A1>
    • atom2: IAtom<A2>
    • atom3: IAtom<A3>
    • atom4: IAtom<A4>
    • atom5: IAtom<A5>
    • atom6: IAtom<A6>
    • atom7: IAtom<A7>
    • atom8: IAtom<A8>
    • atom9: IAtom<A9>
    • atom10: IAtom<A10>
    • projection: (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>, a6: Readonly<A6>, a7: Readonly<A7>, a8: Readonly<A8>, a9: Readonly<A9>, a10: Readonly<A10>) => T
        • (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>, a6: Readonly<A6>, a7: Readonly<A7>, a8: Readonly<A8>, a9: Readonly<A9>, a10: Readonly<A10>): T
        • Parameters

          • a1: Readonly<A1>
          • a2: Readonly<A2>
          • a3: Readonly<A3>
          • a4: Readonly<A4>
          • a5: Readonly<A5>
          • a6: Readonly<A6>
          • a7: Readonly<A7>
          • a8: Readonly<A8>
          • a9: Readonly<A9>
          • a10: Readonly<A10>

          Returns T

    Returns Projector<T>

    A new projector that will act like an atom (has a value, subscribe, and unsubscribe function). This projector's projection function will be executed when any of the atoms provided are updated.

  • Create a new projector, which will take in atom-like objects as input and a function that combines their values into a new value. Commonly this is referred to as a "computed value." Projectors can be used to many purposes, if the new data is just a unique combination of existing values then it's better to use a Projector which will automatically subscribe to the passed in atom-like objects and update when they update. Coordinating this without a projector is possible but why go through all that extra work?

    NOTE

    Since ReadonlyAtom's don't "start" when they don't have subscribers, Projector tries to play smart and mimic this. If it detects that it received a ReadonlyAtom as an input atom it will not subscribe to it unless the Projector itself has a subscriber. If the ReadonlyAtom is live and running then the Projector will be out of sync with it's current value. To fix this, provide a no-op subscriber to the projector or subscribe to it to receive reactive updates.

    Examples

    Probably the simpleist example of a projector is summing up multiple count atoms.

    const counter1 = atom(0);
    const counter2 = atom(0);
    
    const display1 = document.getElementById("display-counter-1");
    counter1.subscribe((count) => {
      display1.textContent = `Current count: ${count.value}`;
    });
    
    const incr1 = document.getElementById("increment-counter-1");
    incr1.addEventListener("click", () => counter1.update((n) => n + 1));
    
    const display2 = document.getElementById("display-counter-2");
    counter2.subscribe((count) => {
      display2.textContent = `Current count: ${count.value}`;
    });
    
    const incr2 = document.getElementById("increment-counter-2");
    incr2.addEventListener("click", () => counter2.update((n) => n + 1));
    
    const sum = projector(counter1, counter2, (a, b) => a + b);
    
    const sumDisplay = document.getElementById("display-sum");
    sum.subscribe((sum) => {
      sumDisplay.textContent = `Total count: ${sum.value}`;
    });
    

    See it on CodePen.

    Projectors don't always have to be used with multiple atoms, somtimes you just want to be able to have and update data from a single atom in a different way. Like showing the full name for a user.

    const user = atom({
      firstName: 'Peter',
      lastName: 'Programmer',
    });
    
    const fullName = projector(user, u => `${u.firstName} ${u.lastName}`);
    
    const firstNameInput = document.getElementById('first-name');
    firstNameInput.value = user.value.firstName;
    firstNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, firstName: event.target.value }));
    });
    
    const lastNameInput = document.getElementById('last-name');
    lastNameInput.value = user.value.lastName;
    lastNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, lastName: event.target.value }));
    });
    
    const fullNameDisplay = document.getElementById('display-full-name');
    fullNameDisplay.textContent = fullName.value;
    fullName.subscribe(name => {
      fullNameDisplay.textContent = name.value;
    });
    

    See it on CodePen

    A simple and clean way to control how data is rendered, just modify fullName's projection function to, say, change the computed value to last, first instead.

    Type parameters

    • T

    • A1

    • A2

    • A3

    • A4

    • A5

    • A6

    • A7

    • A8

    • A9

    • A10

    • A11

    Parameters

    • atom1: IAtom<A1>
    • atom2: IAtom<A2>
    • atom3: IAtom<A3>
    • atom4: IAtom<A4>
    • atom5: IAtom<A5>
    • atom6: IAtom<A6>
    • atom7: IAtom<A7>
    • atom8: IAtom<A8>
    • atom9: IAtom<A9>
    • atom10: IAtom<A10>
    • atom11: IAtom<A11>
    • projection: (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>, a6: Readonly<A6>, a7: Readonly<A7>, a8: Readonly<A8>, a9: Readonly<A9>, a10: Readonly<A10>, a11: Readonly<A11>) => T
        • (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>, a6: Readonly<A6>, a7: Readonly<A7>, a8: Readonly<A8>, a9: Readonly<A9>, a10: Readonly<A10>, a11: Readonly<A11>): T
        • Parameters

          • a1: Readonly<A1>
          • a2: Readonly<A2>
          • a3: Readonly<A3>
          • a4: Readonly<A4>
          • a5: Readonly<A5>
          • a6: Readonly<A6>
          • a7: Readonly<A7>
          • a8: Readonly<A8>
          • a9: Readonly<A9>
          • a10: Readonly<A10>
          • a11: Readonly<A11>

          Returns T

    Returns Projector<T>

    A new projector that will act like an atom (has a value, subscribe, and unsubscribe function). This projector's projection function will be executed when any of the atoms provided are updated.

  • Create a new projector, which will take in atom-like objects as input and a function that combines their values into a new value. Commonly this is referred to as a "computed value." Projectors can be used to many purposes, if the new data is just a unique combination of existing values then it's better to use a Projector which will automatically subscribe to the passed in atom-like objects and update when they update. Coordinating this without a projector is possible but why go through all that extra work?

    NOTE

    Since ReadonlyAtom's don't "start" when they don't have subscribers, Projector tries to play smart and mimic this. If it detects that it received a ReadonlyAtom as an input atom it will not subscribe to it unless the Projector itself has a subscriber. If the ReadonlyAtom is live and running then the Projector will be out of sync with it's current value. To fix this, provide a no-op subscriber to the projector or subscribe to it to receive reactive updates.

    Examples

    Probably the simpleist example of a projector is summing up multiple count atoms.

    const counter1 = atom(0);
    const counter2 = atom(0);
    
    const display1 = document.getElementById("display-counter-1");
    counter1.subscribe((count) => {
      display1.textContent = `Current count: ${count.value}`;
    });
    
    const incr1 = document.getElementById("increment-counter-1");
    incr1.addEventListener("click", () => counter1.update((n) => n + 1));
    
    const display2 = document.getElementById("display-counter-2");
    counter2.subscribe((count) => {
      display2.textContent = `Current count: ${count.value}`;
    });
    
    const incr2 = document.getElementById("increment-counter-2");
    incr2.addEventListener("click", () => counter2.update((n) => n + 1));
    
    const sum = projector(counter1, counter2, (a, b) => a + b);
    
    const sumDisplay = document.getElementById("display-sum");
    sum.subscribe((sum) => {
      sumDisplay.textContent = `Total count: ${sum.value}`;
    });
    

    See it on CodePen.

    Projectors don't always have to be used with multiple atoms, somtimes you just want to be able to have and update data from a single atom in a different way. Like showing the full name for a user.

    const user = atom({
      firstName: 'Peter',
      lastName: 'Programmer',
    });
    
    const fullName = projector(user, u => `${u.firstName} ${u.lastName}`);
    
    const firstNameInput = document.getElementById('first-name');
    firstNameInput.value = user.value.firstName;
    firstNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, firstName: event.target.value }));
    });
    
    const lastNameInput = document.getElementById('last-name');
    lastNameInput.value = user.value.lastName;
    lastNameInput.addEventListener('input', event => {
      user.update(oldUser => ({ ...oldUser, lastName: event.target.value }));
    });
    
    const fullNameDisplay = document.getElementById('display-full-name');
    fullNameDisplay.textContent = fullName.value;
    fullName.subscribe(name => {
      fullNameDisplay.textContent = name.value;
    });
    

    See it on CodePen

    A simple and clean way to control how data is rendered, just modify fullName's projection function to, say, change the computed value to last, first instead.

    Type parameters

    • T

    • A1

    • A2

    • A3

    • A4

    • A5

    • A6

    • A7

    • A8

    • A9

    • A10

    • A11

    • A12

    Parameters

    • atom1: IAtom<A1>
    • atom2: IAtom<A2>
    • atom3: IAtom<A3>
    • atom4: IAtom<A4>
    • atom5: IAtom<A5>
    • atom6: IAtom<A6>
    • atom7: IAtom<A7>
    • atom8: IAtom<A8>
    • atom9: IAtom<A9>
    • atom10: IAtom<A10>
    • atom11: IAtom<A11>
    • atom12: IAtom<A12>
    • projection: (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>, a6: Readonly<A6>, a7: Readonly<A7>, a8: Readonly<A8>, a9: Readonly<A9>, a10: Readonly<A10>, a11: Readonly<A11>, a12: Readonly<A12>) => T
        • (a1: Readonly<A1>, a2: Readonly<A2>, a3: Readonly<A3>, a4: Readonly<A4>, a5: Readonly<A5>, a6: Readonly<A6>, a7: Readonly<A7>, a8: Readonly<A8>, a9: Readonly<A9>, a10: Readonly<A10>, a11: Readonly<A11>, a12: Readonly<A12>): T
        • Parameters

          • a1: Readonly<A1>
          • a2: Readonly<A2>
          • a3: Readonly<A3>
          • a4: Readonly<A4>
          • a5: Readonly<A5>
          • a6: Readonly<A6>
          • a7: Readonly<A7>
          • a8: Readonly<A8>
          • a9: Readonly<A9>
          • a10: Readonly<A10>
          • a11: Readonly<A11>
          • a12: Readonly<A12>

          Returns T

    Returns Projector<T>

    A new projector that will act like an atom (has a value, subscribe, and unsubscribe function). This projector's projection function will be executed when any of the atoms provided are updated.

Const readonlyAtom

  • readonlyAtom will create a new ReadonlyAtom instance with the provided initial value and start function. Readonly atoms are atoms that do not allow users to set or modify values, just read; however, this does not mean their value has to be static. If only an initialValue is provided then the returned readonly atom will have a static value, however if you also pass in a start function, this will receive a set function as an argument and from here you can do whatever you might need, such as running a timeout or interval or any other mechanism you need to periodically update the atoms value.

    Examples

    A really common example case is a clock:

    const clock = readonlyAtom(new Date(), (set) => {
      set(new Date());
    
      const interval = setInterval(() => {
        set(new Date());
      }, 1000);
    
      return () => {
        clearInterval(interval);
      };
    });
    
    const dateTimeDisplay = document.getElementById("date-time");
    
    clock.subscribe((atom) => {
      dateTimeDisplay.textContent = atom.value.toLocaleString();
    });
    

    See it on CodePen.

    Another possible example would be to handle other values set by something else, such as the window scroll event, which can be throttled to prevent unnecessary spam.

    const scrollContainer = document.body;
    const scrollTop = readonlyAtom(scrollContainer.scrollTop, (set) => {
      set(scrollContainer.scrollTop);
    
      const onScroll = throttle(
        () => {
          set(scrollContainer.scrollTop);
        },
        200,
        true
      );
    
      scrollContainer.addEventListener("scroll", onScroll);
    
      () => {
        scrollContainer.removeEventListener("scroll", onScroll);
      };
    });
    
    const display = document.getElementById("scroll-value");
    
    scrollTop.subscribe((atom) => {
      display.textContent = Math.round(atom.value);
    });
    

    See it on CodePen.

    Type parameters

    • T

      The type of the atom's value.

    Parameters

    • initialValue: T

      The initial value of the ReadonlyAtom.

    • Optional start: ReadonlyAtomStarter<T>

      The start function that should run if the ReadonlyAtom is live or has subscribers.

    Returns ReadonlyAtom<T>

    The ReadonlyAtom instance that can be used as readonly storage.

Generated using TypeDoc