npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

use-structure

v1.0.0

Published

Hooks for working with basic JavaScript data structures

Downloads

9

Readme

useStructure

Use Objects, Arrays, Maps, and Sets in the imperative way, but keep React State updated correctly.

Your mutative calls to arrays will be intercepted so that they're immutative, and your component gets updated correctly.

Install

npm install use-structure

useStructure Usage

Objects

import { useStructure } from "use-structure";

function MyComponent() {
  const state = useStructure({
    name: "",
    email: "",
  });

  return (
    <form>
      <input
        onChange={(e) => {
          state.name = e.target.value;
        }}
        value={state.name}
      />
      <input
        onChange={(e) => {
          state.email = e.target.value;
        }}
        value={state.email}
      />
    </form>
  );
}

The assignment operator myObj.val = newVal will update your object here.

Arrays

...
const arr = useStructure([]);
...
<button onClick={() => {
    arr.push(1, 2, 3);
}}>Add value</button>

All the mutuative array methods (push, pop, shift, unshift, sort, splice, reverse, copyWith, and fill) will work as you expect them to in this context.

Sets

...
const set = useStructure(new Set());
...
<button onClick={() => {
    set.add("hello");
}}>Add to set</button>

add, delete, and clear will update your set.

Maps

...
const map = useStructure(new Map());
...
<button onClick={() => {
    map.set('a', 1);
}}>Add to map</button>
...

set, delete, and clear will update your map.

Nested data structures

const state = useStructure({
    people: [
        new Map(
            ['name', ''],
            ['addresses', [{ street: '', city: ''}]],
        )
    ]
});
...
<input onChange={() => {
    state.people[i].get('addresses')[j].city = 'SF';
}} value={state.people[i].get('addresses')[j]}/>

Use any number of nested Objects, Sets, Maps, and Arrays. All of the appropriate methods will still work correctly.

Class instances

class Counter {
  constructor(initialVal = 0) {
    this.count = initialVal;
  }

  increment() {
    this.count += 1;
  }

  decrement() {
    this.count -= 1;
  }
}

function MyComponent() {
    const counter = useStructure(() => new Counter());

    return (
        <h1>{counter.count}</h1>
        <button onClick={() => counter.increment()}>increment</button>
        <button onClick={() => counter.decrement()}>decrement</button>
    )
}

Note that if your class method returns a value, it should not be undefined (use null instead). If it returns undefined, it's an indication that you don't want to cause a rerender (you just want to derive some value).

createGlobalStructure

createGlobalStructure allows you to use state from useStructure across multiple components, without having to use Context.

const useCounter = createGlobalStructure({ counter: 0 });

function Component1() {
  const counter = useCounter();

  return (
    <div>
      <h1>Count: {counter.count}</h1>
      <button
        onPress={() => {
          counter.count += 1; // State changes here update both Component1 and Component2
        }}
      >
        Increment
      </button>
    </div>
  );
}

function Component2() {
  const counter = useCounter();

  return (
    <div>
      <h1>Same Count: {counter.count}</h1> // This will update when button on
      Component1 is pressed
    </div>
  );
}

Observer

createGlobalStructure accepts a second optional boolean value, which will cause the function to return a tuple--the first value will be the the same generated hook, the second is an observer that you can use to subscribe callbacks to, or force updates.

const [useCounter, counterObserver] = createGlobalStructure({ counter}, true);

counterObserver.subscribe(counter => {
  console.log(counter.count); // Will log the count whenever the button on Component1 is pressed
})

...

function Component1() {
    const counter = useCounter();

  return (
    <div>
      <h1>Count: {counter.count}</h1>
      <button onPress={() => {
          counter.count += 1;
        }}
      >Increment</button>
    </div>
  )
}

...

function UpdateCounter(count) {
  counterObserver.update({ count }); // Can be called from anywhere in the app and components using `useCounter` will update.
}

Caveats

  1. Don't use this if you need to support pre ES6 browsers. This package depends on Proxy, which can't be polyfilled.

  2. If you're making multiple updates on nested structures in a single render, always update in order of inner structures to outer structures. Otherwise, you'll overwrite your inner structures and those changes won't be updated on your component.

  3. Don't use cyclical graphs. Though it likely could be supported, it's an edge case I don't want to try to address.

  4. If you use a class instance, your mutating methods must not return anything/return undefined, and your "getters" must not return undefined (use null instead`). This is to prevent render loops.

  5. Any methods on your objects shouldn't be async. Your components won't update correctly if you're trying to set state from within those promises.