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-bloc

v2.4.1

Published

"React useBloc custom hook to use with BLoC pattern"

Downloads

4

Readme

use-bloc

React useBloc custom hook to be used with BLoC pattern

Introduction:

This hook is used for decoupling the business logic of the component using the BLoC pattern. The implementation of the BLoC class "reactiveness" is open to use any library, custom pub/sub, etc. However, MobX is recommended for it's simplicity and performance. An implementation example can be found in this article.

The useBloc hook returns a BLoC instance from the provided "constructor" and "props" in the first and second parameters. It keeps the same instance for the lifetime of the component, or re-instantiates it automatically if props used for state change (stateProps).

There are 2 ways of using the hook:

Automatic BLoC props update

It updates automatically the props in the BLoC in each re-render.

const bloc = useBloc(MyComponentBloc, props, {
  stateProps: ['myStateProp'],
});

Manual BLoC props update

The update of the props should be done manually by defining a updateProps function in the BLoC class, to update the provided props in the argument manually.

const bloc = useBloc(MyComponentBloc, props, ['myStateProp']);
class MyComponentBloc {
  updateProps(props: MyComponentProps) {
    // manually update props
  }
}

Manual update can be forced even if there are no state props (even if the bloc doesn't define a updateProps) by using an empty array, since otherwise the default behaviour if no 3rd param is provided is to do automatic updates.

const bloc = useBloc(MyComponentBloc, props, []);

Automatic BLoC props update Rules:

This allows to reduce the boilerplate and code repetition when creating BLoC classes. For that to work properly these rules should be followed:

  • The BLoC class should define properties with the same name as the props that are not used for state.
  • Props that are used for state should be provided in the stateProps property of the options param. Using options parameter
  • Props that have default values should be provided in the defaults property of the options param. Using options parameter

Usage:

This is a minimal example that does not have state props, since the reactive handling of state changes depends on the library or custom implementation used. If the component is stateless and doesn't have any business logic then it probably doesn't need a BLoC.

JavaScript example:

import React from 'react';
import { useBloc } from 'use-bloc';

class MyComponentBloc {
  constructor(props) {
    this.title = props.title;
  }
}

const MyComponent = (props) => {
  const bloc = useBloc(MyComponentBloc, props);
  return (
    <div>
      <h1>{bloc.title}</h1>
    </div>
  );
};

TypeScript example:

import React from 'react';
import { useBloc } from 'use-bloc';

interface MyComponentProps {
  title: string;
}

class MyComponentBloc {
  title: string;
  constructor(props: MyComponentProps) {
    this.title = props.title;
  }
}

const MyComponent = (props: MyComponentProps) => {
  const bloc = useBloc(MyComponentBloc, props);
  return (
    <div>
      <h1>{bloc.title}</h1>
    </div>
  );
};

Using Automatic BLoC options parameter

Components may define props that would be used for state, like for example to set the initial value. If one of those prop changes, the BLoC instance needs to be re-created since the initial value is now different. For that, the options parameter and stateProps should be used by passing an array of those prop keys that are used for state. This also applies to manual update mode when providing an array of stateProps instead of the options.

Components may have optional props with default values in the BLoC class. These default props should be passed in the defaults property of the options parameter, so they are used if needed when updating the props after a re-render.

An example of using stateProps and defaults in a Checkbox Component (demo example without reactive state handling):

import React from 'react';
import { useBloc } from 'use-bloc';

// Checkbox BLoC
interface CheckboxProps {
  checked?: boolean;
  label?: string;
  onChange?: OnChange;
}

const defaults: Partial<CheckboxProps> &
  Required<Pick<CheckboxProps, 'checked' | 'label'>> = {
  checked: false,
  label: 'Click me!',
};

class CheckboxBloc {
  checked: boolean;
  label: string;
  onChange?: OnChange;

  constructor(props: CheckboxProps) {
    this.checked = props.checked ?? defaults.checked;
    this.label = props.label ?? defaults.label;
    this.onChange = props.onChange;
  }

  toggleChecked() {
    this.checked = !this.checked;
    if (this.onChange) {
      this.onChange(this.checked);
    }
  }
}
// Checkbox component
const Checkbox = (props: CheckboxProps) => {
  const bloc = useBloc(CheckboxBloc, props, {
    stateProps: ['checked'],
    defaults,
  });
  // ...
};

Reducing initial BLoC props initialization boilerplate

When using Automatic BLoC mode, instead of manually initializing the BLoC props like in the previous example, hydrateBloc can be used:

import { hydrateBloc } from 'use-bloc';

interface CheckboxProps {
  checked?: boolean;
  label?: string;
  onChange?: OnChange;
}

const defaults: Partial<CheckboxProps> &
  Required<Pick<CheckboxProps, 'checked' | 'label'>> = {
  checked: false,
  label: 'Click me!',
};

class CheckboxBloc {
  checked!: boolean;
  label!: string;
  onChange?: OnChange;

  constructor(props: CheckboxProps) {
    hydrateBloc(this, props, defaults);
  }

  toggleChecked() {
    this.checked = !this.checked;
    if (this.onChange) {
      this.onChange(this.checked);
    }
  }
}

Note that in TypeScript when defining the properties in the BLoC Class the definite assignment assertion ! would need to be used (depending on TypeScript configuration) since the props are not longer directly initialized in the constructor.

Init BLoC

When the component associated to the BLoC is first rendered, or a "state prop" changes causing the BLoC to be re-instantiated, the init method would be called if it's defined in the BLoC. A useful scenario for using this is on a component that calls an API endpoint on init. It would be preferable to use the init method instead of doing the API call in the constructor so it can be easily unit-tested.

class MyComponentBloc {
  async init() {
    // API call
  }
}

Disposing resources

When the component associated to the BLoC is removed from the DOM, if the BLoC has subscriptions, timers, references, etc. they would need to be disposed. For that there is an optional dispose method that the BLoC class can implement if needed.

class MyComponentBloc {
  dispose() {
    // dispose subscriptions, timers, references, etc
  }
}