GithubHelp home page GithubHelp logo

mobx-light-form's Introduction

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.

예시 이미지

🔍️ 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.

mobx-light-form's People

Contributors

bluelion2 avatar pewww avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

bluelion2

mobx-light-form's Issues

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.