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

@tjaybroodryk/react-viewmodel

v1.1.1

Published

A simple state management library for React using a view-model pattern.

Downloads

36

Readme

ViewModel State Management with React and TypeScript

This project aims to provide an simple and efficient ViewModel pattern using proxy objects to manage class-based state objects. It allows you to pass a class as state, encapsulating your business logic within methods, and ensuring that only subscribed components re-render when a specific property changes.


Features

  1. Class-Based State Management: Define a class that holds your state and logic, and use it directly in your components.
  2. Selective Re-Renders: Only components that subscribe to a specific property of the state will re-render when that property changes, optimizing performance.
  3. ViewModel Pattern: This implementation closely follows the ViewModel pattern, making it ideal for applications that require clear separation between business logic and UI.

Installation

To install the necessary dependencies:

npm install @tjaybroodryk/react-viewmodel

API Overview

1. useViewModel

This hook takes an instance of your class (state object) and returns a proxy that ensures state updates trigger re-renders only for the necessary properties.

2. useSubscribe

This hook allows a component to subscribe to specific properties of the ViewModel. The component will only re-render when the watched property changes.

3. ProvideViewModel

This is a context provider that supplies the ViewModel to the entire subtree. Use it to pass the state down through your component tree.


Example Usage

Let's walk through how to use the hooks in a simple example.

Step 1: Define Your ViewModel Class

First, create your ViewModel class with properties and methods to manage the state.

class MyViewModel {
  count = 0;
  text = '';
  other = ''
  ['@@properties'] = ['count', 'text']; // Define the properties to watch

  increment() {
    this.count++;
  }

  changeOther(value: string) {
    this.other = value// this does not cause a state update, as it is not declared in the properties array
  }

  setText(newText: string) {
    this.text = newText;
  }
}

Step 2: Use useViewModel and useSubscribe in Your Components

Next, use the useViewModel hook in a top-level component to manage the state, and the useSubscribe hook in child components to react to specific property changes.

import React from 'react';
import { useViewModel, useSubscribe, ProvideViewModel } from './StateContext'; // Assuming hooks are in StateContext.ts

// Component subscribing to the "count" property
const CountComponent = () => {
  const count = useSubscribe((state) => state.count); // Subscribe to count
  return <div>Count: {count}</div>;
};

// Component subscribing to the "text" property
const TextComponent = () => {
  const text = useSubscribe<MyViewModel, string>((state) => state.text); // Subscribe to text
  return <div>Text: {text}</div>;
};

// Main component
const App = () => {
  const viewModel = new MyViewModel();

  return (
    <ProvideViewModel model={viewModel}>
      <h1>ViewModel with Selective Re-Renders</h1>
      <CountComponent />
      <TextComponent />
      <StateActions />
    </ProvideViewModel>
  );
};

// Component to trigger state changes
const StateActions = () => {
  const viewModel = useViewModel<MyViewModel>();

  return (
    <div>
      <button onClick={() => viewModel.increment()}>Increment Count</button>
      <button onClick={() => viewModel.setText('Hello World')}>Set Text</button>
    </div>
  );
};

export default App;

Step 3: Render the App

Now render the App component. The ProvideViewModel context provider passes the ViewModel down the tree. The child components (CountComponent and TextComponent) will only re-render when their respective subscribed properties (count and text) change.


API Documentation

useViewModel

function useViewModel<T>(initialState: T): T;
  • Purpose: Creates a proxy for your state, handling updates and ensuring only the relevant components re-render when state properties change.
  • Parameters: initialState – The ViewModel class instance (e.g., new MyViewModel()).
  • Returns: A proxied state object that can be used in components.

useSubscribe

function useSubscribe<T, P>(selector: (state: T) => P): P;
  • Purpose: Subscribes a component to a specific property from the ViewModel. The component will re-render only when the selected property changes.
  • Parameters:
    • selector: A function that selects a specific property from the ViewModel (e.g., (state) => state.count).
  • Returns: The selected property.

ProvideViewModel

function ProvideViewModel<T>({ children, initialState }: { children: ReactNode; initialState: T }): JSX.Element;
  • Purpose: Provides the ViewModel to the entire React tree below it using React Context.
  • Parameters:
    • children: The child components that should have access to the ViewModel.
    • initialState: The ViewModel instance to be shared (e.g., new MyViewModel()).
  • Returns: A JSX.Element that wraps the child components with the ViewModel context.

Tests

To ensure the behavior of the ViewModel and hooks, tests are written using Vitest. The test suite checks:

  1. Initial state setup.
  2. Selective re-renders of subscribed properties.
  3. Non-re-rendering when unrelated properties change.

To run tests:

yarn test

Conclusion

This ViewModel approach is a clean and efficient way to manage state in React, particularly in complex applications. By subscribing only to specific properties, you can optimize performance by preventing unnecessary re-renders. This approach also fits well with the ViewModel pattern, enabling you to keep business logic within the state class.