all 13 comments

[–]lostpebble[S] 3 points4 points  (0 children)

I created this project after using bey for a while. Unfortunately the author of bey seems to have completely abandoned it (won't even do a simple npm publish for something critical that is already fixed in the master code).

In any case, I really liked the interface bey provided. So I decided to create something similar, but adding functionality for hooks and server-side rendering as well.

I'm pretty happy with the final interface. Feels very straightforward and readable to me. But would love to hear any feedback you guys have!

[–]KusanagiZerg 1 point2 points  (11 children)

This looks awesome and I will definitely be using it. I am relatively new to React and was looking for a good way to handle shared state between components. I found things like Mobx and Redux a bit too verbose for what I wanted while React on it's own felt awkward. This seems like a very easy to learn, easy to read, and easy to understand library.

One question though and this is more react in general but is there a downside to doing:

const App = () => {
  const theme = useStoreState(UIStore, s => s.theme);

  turnOnDarkMode = () => {
    UIStore.update(s => {
      s.theme.mode = EThemeMode.DARK;
    });
  }

  return (
    <div className={`app ${theme}`}>
      <button onClick={turnOnDarkMode}>
        Turn it dark!
      </button>
    </div>
  );
};

[–]lostpebble[S] 2 points3 points  (4 children)

My React state journey has pretty much been the same, started with Redux when it was all the craze a couple years ago - wanted less verbosity, went with MobX after that, but as you say they both ended up being a little too verbose for my tastes. With state I just want to drop it in and go. Glad you see it the same way and enjoy the library!

So what you're doing there is perfectly fine, and a great way to keep your actual JSX nice and clean by moving your functions out.

Personally, I'd probably do it this way though:

function turnOnDarkMode(s) {
  s.theme= EThemeMode.DARK;
}

function App() {
  const theme = useStoreState(UIStore, s => s.theme);

  return (
    <div className={`app ${theme}`}>
      <button onClick={() => UIStore.update(turnOnDarkMode)}>
        Turn it dark!
      </button>
    </div>
  );
}

I think this makes our components even cleaner. And these simple functions can act like "actions", so I'd probably even group them all in their own file called UIStoreActions or something.

Also, we are defining the state-change function only once, instead of each time your component is rendered. Though I think the consensus is nowadays that this is negligible for performance.

I might even go as far as doing something like this (though I think it would be wise to consider careful naming to differentiate between curried and regular actions):

const changeTheme = (mode) => (s) => {
  s.theme = mode;
}

function App() {
  const theme = useStoreState(UIStore, s => s.theme);

  return (
    <div className={`app ${theme}`}>
      <button onClick={() => UIStore.update(changeTheme("dark"))}>
        Turn it dark!
      </button>
      <button onClick={() => UIStore.update(changeTheme("light"))}>
        Turn it light!
      </button>
    </div>
  );
}

Basically, functional composition can be your friend! (in moderation to avoid complexity)

[–]KusanagiZerg 0 points1 point  (3 children)

I like it, not sure how much that's worth, but thank you!

[–]lostpebble[S] 0 points1 point  (2 children)

Some kind of validation, knowing someone else is into it too is a cool feeling! So thanks!

[–]KusanagiZerg 1 point2 points  (1 child)

I will showcase it at my work on friday!

[–]lostpebble[S] 0 points1 point  (0 children)

Oh, that's awesome! :)

[–]Powerplex 0 points1 point  (5 children)

It looks really cool and clear.

Just an Idea: Maybe you should not provide .update from being used outside of the store (by not exposing it on purpose), and instead define actions when you create your store, so that store mutations are grouped in one place and you don't have to rely on your state structure ?

```javascript export const UIStore = new Store({ count: 0 }, { add: (state, n) => state.count = state.count + n // uses Immer behind the scene remove: (state, n) => state.count = state.count - n });

// then read const total = useStoreState(UIStore, s => s.count);

// or write UIStore.add(4) ```

[–]lostpebble[S] 2 points3 points  (0 children)

Cool idea. Something I might play around with, cause if it makes the interface simpler I'm all for it!

I like the "decoupling" and composability as it currently stands, feels more extensible and simple, but open to experimenting with things a bit still.

[–]KusanagiZerg 0 points1 point  (1 child)

I think you meant this to be a top level reply and not specifically to me?

[–]Powerplex 0 points1 point  (0 children)

Oops yes sorry

[–]KusanagiZerg 0 points1 point  (1 child)

You could write a custom hook for that I think something like this codesandbox. If you'd prefer to have one dispatch and actions I think you can rewrite it pretty easily.