DEV Community

Cover image for Introducing Object Hooks: Mutable State for React 🤯
Ryan Lynch (he/him)
Ryan Lynch (he/him)

Posted on

Introducing Object Hooks: Mutable State for React 🤯

The idea I started with is: What if we could write state changes in react as mutations on a persistent reference? 🤔 So I wrote the code to make it work! The basic concept is summed up in the code from the banner image:

import React from 'react';

import { useObject } from '../hooks/useObject';

export const Counter: React.FC = () => {
  const [state] = useObject({
    count: 1,
  });

  return (
    <button
      onClick={() => {
        state.count++;
      }}
    >
      {state.count}
    </button>
  );
};
Enter fullscreen mode Exit fullscreen mode

In this case, state is a persistent reference that you can mutate (state.count++). The consequences of this change are fairly interesting. For one, you can create these objects with a class! So the above example could be rewritten as:

import React from 'react';

import { useInstance } from '../hooks/useInstance';

class Counter {
  count = 1;

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

export const Counter: React.FC = () => {
  const [counter] = useInstance(Counter);

  return (
    <button
      onClick={() => {
        counter.increment();
      }}
    >
      {counter.count}
    </button>
  );
};
Enter fullscreen mode Exit fullscreen mode

Also, if you need to subscribe to to changes in the objects outside of a component function, they implement the AsyncIterable interface, so you can await their next state, or for await future states in an async function. Here's a logger class that subscribes to a counter!

class CounterLogger {
  constructor(
    private counter: Counter & AsyncIterable<Counter>
  ) {}

  log(count: number) {
    console.log(`Count is: ${count}`);
  }

  async watch() {
    this.log(this.counter.count);

    for await (const { count } of this.counter) {
      this.log(count);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

There's more to it, and there will be more as the project progresses. I wanted to share what I have in its current "state" 😉 to give everyone a chance to contribute their thoughts and ideas! If you're interested, clone the repo and try it for yourself! Or leave a comment or question on this post! All are contributions, all are welcome. 🔰

Discussion (7)

Collapse
yannickfricke profile image
Yannick Fricke

Hey Ryan!

Someone else posted a link to the GitHub repository yesterday and I quickly took a look on it! Seems very interesting! I also had some thoughts about a resettable state and published my code :D

github.com/YannickFricke/react-res...

Feel free to leave any comments! :)

Collapse
shiftyp profile image
Ryan Lynch (he/him) Author

I like it! I use a similar pattern in object hooks!

Collapse
yannickfricke profile image
Yannick Fricke

To be honest, I was inspired by your library! :)

Thread Thread
shiftyp profile image
Ryan Lynch (he/him) Author

A great library would be so inspiring and accessible that you could and would want to adapt parts of it or ideas from it for your own use. I'm happy it resonated with you, and you were able to pick up on the pattern and adapt it!

Thread Thread
yannickfricke profile image
Yannick Fricke

I just wanted this single part only without all the other stuff you have in your code.

KISS - Keep It Stupid Simple

Someone could argue that relying on to much dependencies makes the technical debt bigger and bigger. But this is so simple, you could even write it yourself :D

Thanks again for the idea! :)

Thread Thread
shiftyp profile image
Ryan Lynch (he/him) Author

The extra bit that bridges the diff between resettable state, and mutable state: is taking the setState function and calling it whenever properties on the object change. If that's not a feature you want though, then resettable state is a good solution!

Collapse
tylerlwsmith profile image
Tyler Smith

Yo Ryan this is awesome!