Giter Club home page Giter Club logo

formal's Introduction

Kevin Wolf formal

Watch on GitHub Star on GitHub Tweet!

Elegant cross-platform form management primitives
for the react hooks era.

PRs Welcome Build Status Code Coverage Greenkeeper

@kevinwolf/formal @kevinwolf/formal

Problem

Working with forms on react can be a really repetitive task. Most of the existing abstractions provides a render props API and it is just not cool on the react hooks era.

Also, some of those packages does not provide out of the box support for both web and mobile platforms.

Solution

Formal is a cross-platform solution that exposes just the right primitives you need to manage your forms state and validations with ease.

Table of Contents

Installation

Add this package to your application dependencies with yarn:

yarn add @kevinwolf/formal
# npm install --save @kevinwolf/formal

Usage

This package exports by default the useFormal hook, so you only need to import it and hook it up with your initial state and an options.

React Web

import React from "react";
import useFormal from "@kevinwolf/formal";

const initialValues = {
  firstName: "Tony",
  lastName: "Stark",
  email: "[email protected]"
};

function App() {
  const formal = useFormal(initialValues, {
    onSubmit: values => console.log("Your values are:", values)
  });

  return (
    <form onSubmit={formal.handleSubmit}>
      <div>
        <label htmlFor="firstName">First Name</label>
        <input {...formal.getFieldProps("firstName")} type="text" />
      </div>

      <div>
        <label htmlFor="lastName">Last Name</label>
        <input {...formal.getFieldProps("lastName")} type="text" />
      </div>

      <div>
        <label htmlFor="email">Email</label>
        <input {...formal.getFieldProps("email")} type="text" />
      </div>

      <button type="submit">Subnit</button>
    </form>
  );
}

React Native

import React from "react";
import { View, Text, TextInput, Button } from "react-native";
import useFormal from "@kevinwolf/formal";

const initialValues = {
  firstName: "Tony",
  lastName: "Stark",
  email: "[email protected]"
};

function App() {
  const formal = useFormal(initialValues, {
    onSubmit: values => console.log("Your values are:", values)
  });

  return (
    <View>
      <View>
        <Text>First Name</Text>
        <TextInput {...formal.getFieldProps("firstName")} />
      </View>

      <View>
        <Text>Last Name</Text>
        <TextInput {...formal.getFieldProps("lastName")} />
      </View>

      <View>
        <Text>Email</Text>
        <TextInput {...formal.getFieldProps("email")} />
      </View>

      <Button title="Submit" onPress={formal.handleSubmit} />
    </View>
  );
}

API Reference

useFormal

Main useFormal hook. Use it on your functional components to get the primitives to easily work with forms.

export default function useFormal<FormalValues>(
  initialValues: FormalValues,
  config: FormalConfig<Values>
): FormalState<Values>;

FormalValues

The form initial values. It can be a hardcoded object or an object gotten from an API endpoint.

type InitialValues = {
  [field: string]: any;
};

Example:

const initialValues = {
  firstName: "Tony",
  lastName: "Stark",
  email: "[email protected]"
};

FormalConfig

The hook configuration object.

import { Schema } from "yup";

interface FormalConfig<FormalValues> {
  schema?: Schema<FormalValues>;
  onSubmit: (
    values: FormalValues,
    formal: FormalState<Values>
  ) => void | Promise<any>;
}
schema

A yup schema definition. It will be called before submitting the form.

onSubmit

The function that will be called if your form is correctly validated, passing the actual values as the first argument and the FormalState as the second argument. If it is an asynchronous function, then formal.isLoading will be true until the promise is resolved or rejected.

Example:

import * as yup from "yup";

const schema = yup.object().shape({
  firstName: yup.string().required(),
  lastName: yup.string().required(),
  email: yup
    .string()
    .email()
    .required()
});

async function onSubmit(values, formal) {
  try {
    await someAsyncTask(values);
  } catch (errors) {
    const formattedErrors = transformErrorsForFormal(errors);
    formal.setErrors(formattedErrors);
  }
}

const formalConfig = {
  schema,
  onSubmit
};

FormalState

This is the state, callbacks, flags and prop getters returned by useFormal() hook.

interface FormalState<Values> {
  // Flags
  isValid: boolean;
  isDirty: boolean;
  isSubmitting: boolean;
  isSubmitted: boolean;

  // State
  values: Values;
  errors: null | FormalErrors<Values>;

  // Callbacks
  handleChange: (field: keyof Values, value: any) => void;
  handleReset: () => void;
  handleSubmit: () => void;
  setErrors: (errors: FormalErrors<Values>) => void;

  // Prop getters
  getFormProps: () => FormalFormProps;
  getFieldProps: (field: keyof Values) => FormalFieldProps;
  getResetButtonProps: () => FormalResetButtonProps;
  getSubmitButtonProps: () => FormalSubmitButtonProps;
}
isValid

A boolean indicating if the schema is valid or not.

isDirty

A boolean indicating if the form has changed since is initial state or its latest successful submission.

isSubmitting

A boolean indicating if the form is being submitted.

isSubmitted

A boolean indicated if the form has already been submitted.

values

The current form values.

errors

The current form errors.

handleChange

Programatically change the value of a field.

formal.handleChange("firstName", "New First Name");
handleReset

Programatically reset the form to its initial state or last successful values in case it has been submitted.

formal.handleReset();
handleSubmit

Programatically submit the form.

formal.handleSubmit();
getFormProps

Returns the props to spread to a form element. Wraps formal.handleSubmit method.

NOTE: Since React Native does not have a <form /> equivalent, this method will throw an error.

Example:

formal.getFormProps = () => ({
  onSubmit: e => {
    e.preventDefault();
    formal.handleSubmit();
  };
})

<form {...formal.getFormProps()} />;
getFieldProps

Returns the props to spread to a form field. Wraps formal.values[field] value and formal.handleChange method.

formal.getFieldProps = field => ({
  name: field,
  id: field,
  value: formal.values[field],

  // On React Web:
  onChange: e => {
    e.preventDefault();
    formal.handleChange(field, e.target.value);
  },

  // On React Native:
  onChangeText: text => {
    formal.handleChange(field, text);
  }
});

// React Web:
<input {...getInputProps('firstName')} type="text" />

// React Native:
<TextInput {...getInputProps('firstName')} />
getResetButtonProps

Useful if you have a reset button on your form. Wraps handleReset method.

formal.getResetButtonProps = () => ({
  // On React Web:
  onClick: () => {
    formal.handleReset();
  },

  // On React Native:
  onPress: () => {
    formal.handleReset();
  }
});

// React Web:
<button {...formal.getResetButtonProps()}>Reset form</button>

// React Native:
<Button {...formal.getResetButtonProps()} title="Reset form" />
getSubmitButtonProps

Returns the props to spread to a submit button. Wraps formal.isValid, formal.isDirty and formal.isSubmitting flags and formal.handleSubmit method.

formal.getSubmitButtonProps = () => ({
  disabled: !formal.isValid || !formal.isDirty || formal.isSubmitting,

  // On React Web:
  onClick: () => {
    formal.handleSubmit()
  },

  // On React Native:
  onPress: () => {
    formal.handleSubmit();
  }
});

// React Web:
<button {...formal.getSubmitButtonProps()}>Submit form</button>

// React Native:
<Button {...formal.getSubmitButtonProps()} title="Submit form" />

Contributing

If you have any question, suggestion or recommendation, please open an issue about it.

If you want to purpose some changes create a pull request.

Installing the project

  1. git clone [email protected]:kevinwolfcr/formal.git && cd $_.
  2. Install dependencies yarn install.

Running the development environment

This library uses storybook to visually document itself.

Running the tests

To run the tests run yarn test or yarn test --watch to watch for changes. Please write some tests if you are changing anything ๐Ÿ™๐Ÿป.

Validating the changes

Although this is connected to TravisCI, it's encourage to run yarn validate in order to make sure everything works as expected.

Other solutions

This is heavily inspired on formik, which currently does not support react hooks. If you are aware or maintain a similar solution, please let me know.

License

MIT

formal's People

Stargazers

 avatar

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.