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

mobx-light-form

v1.0.5

Published

Manage React forms state and automatic validation with mobx

Downloads

5

Readme

MobX Light Form

✨ MobX Form State Management with automatic validation

Seperate subjects which manage form data to prevent duplication of data and ensure consistency. The basic principle of responsibility allocation is to allocate responsibility to objects with information that can perform responsibility, so that the form can take charge of the transformation of data to be sent from the client to the server.

Demo

🔍️ Features

  • Form update, reset and clear
  • Check form is valid or has some errors
  • To be added

⚙ Install

npm

npm install mobx-light-form

yarn

yarn add mobx-light-form

🚀 Quick Start

1-a. Define Form

import { makeObservable, observable } from 'mobx';

import Form, { FieldSource } from 'mobx-light-form';

export default class PersonForm extends Form {
  public name: FieldSource<string>;

  public nickname: FieldSource<string>;

  constructor() {
    super();

    this.name = this.generateField<string>({
      key: 'name', // Required: Should be same as member field
      label: 'Name',
      isRequired: true, // Should not be empty
      value: '', // Default value,
      validation: [
        /^Pewww.*$/, // Can be Regex or
        (v: string) => [ // function - () => [boolean, ErrorMessage | undefined]
          v === 'Pewwwww',
          "Should be 'Pewwwww'"
        ]
      ]
    });

    const TestAPI = {
      checkIsAvailable: (value: string) => {
        const existingNicknames = [
          'AAA',
          'BBB',
          'CCC',
          'DDD',
          'EEE'
        ];

        return new Promise(resolve => {
          setTimeout(() => {
            resolve(!existingNicknames.includes(value));
          }, 200);
        });
      }
    };

    this.nickname = this.generateField<string>(
      {
        key: 'nickname',
        label: 'Nickname',
        value: '',
        validation: [
          // If you want to use asynchronous validation
          async (v: string) => {
            const isAvailableNickname = await TestAPI.checkIsAvailable(v);

            return [isAvailableNickname, 'Your nickname already exists.'];
          }
        ]
      },
      true // Prevent initial validation
    );

    makeObservable(this, {
      name: observable,
      nickname: observable
    });
  }

  public toDto() { // Convert data to be sent to the server here.
    return {
      personName: this.name.value,
      nickname: this.nickname.value
    };
  }
}

1-b. Define Form with array value

Define another form to be an item of array.

import { makeObservable, observable } from 'mobx';

import Form, { FieldSource } from 'mobx-light-form';

export default class BookForm extends Form {
  public name: FieldSource<string>;

  public author: FieldSource<string>;

  constructor() {
    super();

    this.name = this.generateField<string>({
      key: 'name',
      label: 'Book Name',
      value: '',
      isRequired: true
    });

    this.author = this.generateField<string>({
      key: 'author',
      label: 'Author',
      value: ''
    });

    makeObservable(this, {
      name: observable,
      author: observable
    });
  }
}
import { makeObservable, observable, action } from 'mobx';

import Form, { FieldSource } from 'mobx-light-form';

import BookForm from 'src';

export default class PersonForm extends Form {
  // ...other fields
  public favoriteBooks: BookForm[];

  constructor() {
    super();

    this.favoriteBooks = [];

    makeObservable(this, {
      favoriteBooks: observable,
      addBook: action,
      clearBooks: action
    });
  }

  public addBook() { // If you need, override or create form methods.
    this.favoriteBooks.push(new BookForm());
  }

  public clearBooks() {
    this.favoriteBooks = [];
  }
}

2. Register form in store

import PersonForm from 'src';

export default class PersonStore {
  public personForm: PersonForm;

  constructor() {
    this.personForm = new PersonForm();
  }
}

3. Handle Input values

// Please see Demo

import React, { useCallback } from 'react';
import { observer } from 'mobx-react';
import debounce from 'lodash.debounce';

import { usePersonStores } from '../stores/PersonProvider';

import { Input, Button } from '../components';

const PersonBody = observer(() => {
  const { personStore } = usePersonStores(); // The way you get mobx store
  const { personForm: form } = personStore;

  const handleChange = useCallback(
    (fieldName: string) => (value: string) => {
      form.update({
        [fieldName]: value
      });
    },
    [form]
  );

  const debouncedValidate = useCallback(debounce(() => {
    form.validate(); // Trigger validation when you need
  }, 500), [form]);

  const handleNicknameChange = useCallback((value: string) => {
    form.update(
      {
        nickname: value
      },
      true // Prevent validation
    );

    debouncedValidate();
  }, [form, debouncedValidate]);

  const handleReset = useCallback(() => {
    form.reset();
  }, [form]);

  const handleBookAdd = useCallback(() => {
    form.addBook();
  }, [form]);

  const handleBooksClear = useCallback(() => {
    form.clearBooks();
  }, [form]);

  const handleSubmit = useCallback(() => {
    console.log('Submit Result: ', form.toDto());
  }, [form]);

  return (
    <Wrapper>
      <Input
        label={form.name.label}
        value={form.name.value}
        placeholder="Write name"
        onChange={handleChange("name")}
      />
      {!!form.errors.name && (
        <ErrorMessage>{form.errors.name}</ErrorMessage>
      )}
      <Input
        label={form.nickname.label}
        value={form.nickname.value}
        placeholder="Write nickname"
        onChange={handleNicknameChange}
      />
      {!!form.errors.nickname && (
        <ErrorMessage>{form.errors.nickname}</ErrorMessage>
      )}
      {form.favoriteBooks.map((f) => (
        <FavoriteBookWrapper key={f.__id}>
          <Input
            placeholder="Write author"
            label={f.author.label}
            value={f.author.value}
            onChange={(value) => {
              f.update({
                author: value
              });
            }}
          />
          <Input
            placeholder="Write book name"
            label={f.name.label}
            value={f.name.value}
            className="book-name"
            onChange={(value) => {
              f.update({
                name: value
              });
            }}
          />
        </FavoriteBookWrapper>
      ))}
      <StyledButton onClick={handleReset}>Reset</StyledButton>
      <StyledButton onClick={handleBookAdd}>Add Book</StyledButton>
      <StyledButton onClick={handleBooksClear}>Clear Books</StyledButton>
      <StyledButton onClick={handleSubmit} disabled={!form.isValid}>
        Submit
      </StyledButton>
    </Wrapper>
  );
});

export default PersonBody;

FAQ

1. What is the difference between reset and clear?

If you 'reset', it initializes all fields and then initializes both 'touched' and 'errors' properties. The value of 'isValid' may not be set properly because 'errors' property is initialized(undefined). It is recommended to run the 'validate' method at a certain point in time to check the validation.

On the other hand, If you 'clear', it initialized all fields and then initialized 'touched' property, but it does not initialize 'errors' property. Since validation is checked internally after all fields are initialized, it is recommended to use it when it is necessary to check 'isValid' from the beginning.