Skip to content

πŸ‘” Elegant cross-platform form management primitives for the react hooks era.

License

Notifications You must be signed in to change notification settings

emersonlaurentino/formal

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

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

About

πŸ‘” Elegant cross-platform form management primitives for the react hooks era.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published