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

@joseaburt/live-data

v0.0.7

Published

### Philosophy

Downloads

1

Readme

@joseaburto/live-data

Philosophy

Let's try to put React.js in its place: Library to create user interfaces.


LiveData is an observable data holder class.

This is an observable data structure, that was designed to wrap any other data structure and make it observable. So because of that, when its state change, that new change will be notified to all subscrited observers.

Note that this clase expose not mutator functionality, and also is a not instanciable class (because is abstract). The main idea is that this class will be used to provide a type that will be used in two cases:

  1. Extentions: You want to create your own impls, like pre-built MutableLiveData.
  2. Expose immutable data: You want to expose your state/subject as immutable, and provide a common interface to mutate that data.

In the example below, we can see MutableLiveData<Credentials> which can be mutated, but note that the public getMutableLiveData(): LiveData<Credentials> method exposes that state as immutable because LiveData is immutable, an this is a very important concept because of two important thing:

  1. prevent inconsitent state, and
  2. ensure reactivity.
class LoginViewModel {
  private data: MutableLiveData<Credentials>;
  public getMutableLiveData(): LiveData<Credentials> {
    return this.data;
  }
}

This is possible by using an extention or base principle of the @joseaburt/event-bus that is not more than the Observer Pattern adaptation in combination with a bus.

The idea was for decoupling logic from the any framework or UI library, in this case from React.js library.

This idea was also inspired from Android Ecosystem Architecture & Design.

Example

// Our data to observe
type Credentials = {
  email: string;
  password: string;
};

// Our simple ViewModel
class LoginViewModel {
  private data: MutableLiveData<Credentials>;

  constructor() {
    this.data = new MutableLiveData({ email: '', password: '' });
  }

  public setData(creds: Credentials): void {
    this.data.setSubject(creds);
  }

  public getMutableLiveData(): LiveData<Credentials> {
    return this.data;
  }
}

// ============================================
// Presentation Layer: A simple Form component
// ============================================

// Component Props
interface LoginFormProps {
  state: Credentials;
  setState: (newState: Credentials) => void;
}

// Component that only take care of print view in screen
const Form = (props: LoginFormProps): JSX.Element => {
  const handleOnChange = ({ target }: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = target;
    props.setState({ ...props.state, [name]: value });
  };

  return (
    <form>
      <label htmlFor="email" children="Email" />
      <input name="email" id="email" type="email" value={props.state.email} onChange={handleOnChange} />
      <label htmlFor="password" children="Password" />
      <input name="password" id="password" type="password" value={props.state.password} onChange={handleOnChange} />
    </form>
  );
};

// Some adapter component to integrate/join the ViewModel and the Presenter
function withLoginViewModel(Component: React.ComponentType<LoginFormProps>, viewModel: LoginViewModel): React.ComponentType<{}> {
  return function (): JSX.Element {
    const { state, action } = useMutableLiveData<Credentials, LoginViewModel>(viewModel.getMutableLiveData(), viewModel);
    return <Component state={state} setState={(creds: Credentials) => action.setData(creds)} />;
  };
}

We can act ever, without needed a UI component.

test('should set new data and update subscribers when using viewModel directly', async () => {
  render(<MyLoginForm />);

  expect(screen.getByLabelText('Email')).toHaveValue('');
  expect(screen.getByLabelText('Password')).toHaveValue('');

  const newCreds: Credentials = { password: '1234', email: '[email protected]' };

  act(() => {
    // We are acting using directly the instance of the viewModel that acts as a manager for the data.
    loginViewModel.setData(newCreds);
  });

  await waitFor(() => {
    expect(screen.getByLabelText('Email')).toHaveValue(newCreds.email);
  });

  expect(screen.getByLabelText('Password')).toHaveValue(newCreds.password);
});

// or using components

test('should set new data and update subscribers when using a ui component', async () => {
  render(<MyLoginForm />);

  const emailField = screen.getByLabelText('Email');
  const passwordField = screen.getByLabelText('Password');

  expect(emailField).toHaveValue('');
  expect(passwordField).toHaveValue('');

  // Updating Email Form Field & asserting new state change
  const newEmailValue = '[email protected]';
  useEvent.type(emailField, newEmailValue);
  await waitFor(() => {
    expect(emailField).toHaveValue(newEmailValue);
  });

  // Updating Password Form Field & asserting new state change
  const newPasswordValue = '[email protected]';
  useEvent.type(passwordField, newPasswordValue);
  await waitFor(() => {
    expect(passwordField).toHaveValue(newPasswordValue);
  });
});