Socket
Socket
Sign inDemoInstall

@yanfoo/react-var

Package Overview
Dependencies
3
Maintainers
1
Versions
24
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

    @yanfoo/react-var

Reactive variables for React


Version published
Maintainers
1
Install size
12.4 kB
Created

Readme

Source

Reactive variables npm (scoped) Socket Badge

Observable global state management for React. This project was inspired by jorbuedo's react-reactive-var.

Install

npm i @yanfoo/react-var --save
yarn add @yanfoo/react-var
pnpm i @yanfoo/react-var

Usage

// auth.ts
/*
   This is the context wrapping the global state and exposing a contained API
*/
import reactVar from "@yanfoo/react-var";
//    or
// import { createReactVar } from '@yanfoo/react-var';

type User = {
  id: number;
  username: string;
};

type UserAuthStatus = {
  user: User | null;
  error: string | null;
};

// default comparator is equality : a === b
export const activeUser = reactVar<UserAuthStatus>(
  { user: null, error: null },
  {
    comparator: (a, b) => a.user?.id === b.user?.id && a.error === b.error,
  }
);

// the API
export const authenticator = {
  login: (username: string, password: string): void => {
    // we simulate a network delay...
    setTimeout(() => {
      if (password === "1234") {
        activeUser({ user: { id: 1, username }, error: null }); // simulate a successful login
      } else {
        activeUser(({ user }) => ({
          user,
          error: "Invalid username or password",
        }));
      }
    }, 200); // 200ms
  },
  logout: () => {
    activeUser({ user: null, error: null }); // again, this could be asynchronous
  },
};
// Component.tsx
/*
   This is the component making use of the global state and API
*/
import React from "react";
import { authenticator, activeUser } from "./auth";

export default () => {
  const userRef = React.useRef<HTMLInputElement>(null);
  const passRef = React.useRef<HTMLInputElement>(null);

  const handleLogin = () =>
    authenticator.login(userRef.current?.value, passRef.current?.value);

  return (
    <div>
      <div>
        Currently authenticated user : <UserLabel />
      </div>
      <AuthMessage />
      <div>
        <label for="username">Username</label>
        <input id="username" type="text" ref={userRef} />
      </div>
      <div>
        <label for="username">Password</label>
        <input id="username" type="password" ref={passRef} />
      </div>
      <div>
        <button onClick={handleLogin}>Login</button>
      </div>
    </div>
  );
};

const UserLabel = () => {
  const username = activeUser.useValue(({ user }) => user?.username ?? null);
  //    ^?:string | null

  return username ?? "Guest";
};

const AuthMessage = () => {
  const message = activeUser.useValue(({ error }) => error ?? null);
  //    ^?:string | null

  return message ? <div>{message}</div> : null;
};

Limitations

Setting a new values to a ReactVar can be made asynchronously. Meaning that any modification will cause parallel modifications to be stacked and processed sequentially. However, trying to modify a ReactVar within a subscriber callback will fail, and this is in order to prevent race conditions, where the subscriber and the setter would wait for each other to complete, creating a dead lock situation.

Example
const myVar = reactVar();

myVar.subscribe(({ value }) => {
  myVar(value + 1);
  // Error: Cannot modify ReactVar while it is already being modified!
});

myVar(0);
Workaround

The subscription handler provides an after function to invoke a callback when the mutation has settled. This callback is called asynchronously, and allows to immediately modify the ReactVar instance. If used, this feature should include an exit condition as it will trigger updates as long as the value is evaluated as different by the comparator; using this feature should be avoided if possible.

const myVar = reactVar(0, {
  comparator: (a, b) => Math.abs(a - b) < 0.5,
});

myVar.subscribe(({ value, after }) => {
  // execute this once every changes have settled
  after(() => {
    myVar(myVar.value / 2);
  });
});

myVar(16);
// Will update myVar with the following values until definitely settled:
//    16, 8, 4, 2, 1, 0.5
// After everything is settled, myVar.value = 0.25 because the comparator
// consider equal any value below 0.5

Contribution

All contributions welcome! Every PR must be accompanied by their associated unit tests!

License

MIT

Keywords

FAQs

Last updated on 13 Feb 2023

Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc