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

react-bfm

v2.0.0-beta.18

Published

A basic field / form manager for React using hooks

Downloads

673

Readme

THE DOCUMENTATION FOR THIS LIBRARY IS STILL WORK IN PROGRESS

React BFM

A basic field (or form) state manager for React using hooks.

Features

  • Initialize fields on rendering
  • Configuration by using props
  • Only hooks and state functions: no components
  • Can be used with any component, and customize to your needs, see examples.
  • Dynamically change your form based on rendered components. No need to update your form state/configuration beforehand.
  • No strict formats on validators you want to use. Validators can return strings, array, object, etc. as long a falsy value is returned when the value is valid.
  • The value in the state can be formatted to your needs.

Installation

Installing is simple:

$ yarn add react-bfm
# or
$ npm install react-bfm

Now you can import the most important hook and create your forms: useConnectField()

Documentation

Hooks / Functions

useConnectField( props, [omitProps] )
Connects a field to a form namespace. The props object must include namespace and fieldName.

Extended props are returned by the hook, with the following remarks:

  • Excluded are props only used for the hook: namespace, fieldName, defaultValue, validator, dirtyCheck, transformValueToInput and transformEventToValue
  • Excluded are keys given by the omitProps.
  • Included are onFocus, onChange and onBlur to handle the input field state.

Add the validator prop to validate the field value. This function gets two arguments: value and props. It should return a falsy value if the input value is valid. If the input value is not valid. any truthy value can be returned, for example a string of array with error message(s).

useFieldError( namespace, fieldName )
getFieldError( namespace, fieldName )

The error for a field, as returned by the validator. Returns null if field is valid.

useFieldHasFocus( namespace, fieldName )
hasFieldFocus( namespace, fieldName )

Returns a boolean, true if the field has focus.

useFieldIsDirty( namespace, fieldName )
isFieldDirty( namespace, fieldName )

Returns a boolean, true if the field value has been changed.

useFieldIsTouched( namespace, fieldName )
isFieldTouched( namespace, fieldName )

Returns a boolean, true if the field value has been touched (after first onFocus event).

useFieldIsValid( namespace, fieldName )
isFieldValid( namespace, fieldName )

Returns a boolean, true if the field value is valid (error is null).

useFieldValue( namespace, fieldName )
getFieldValue( namespace, fieldName )

Returns the field state value.

useFieldValueOnFocus( namespace, fieldName )
getFieldValueOnFocus( namespace, fieldName )

Returns the field value what it was before the field got focus. Only available if the field has focus, otherwise null.

useNamespaceErrors( namespace )
getNamespaceErrors( namespace )

Returns an object with the error value of every field in the namespace.

useNamespaceHasFocus( namespace )
hasNamespaceFocus( namespace )

Returns a boolean, true if one field in the namespace has focus.

useNamespaceIsDirty( namespace )
isNamespaceDirty( namespace )

Returns a boolean, true if one or more fields in the namespace is dirty.

useNamespaceIsTouched( namespace )
isNamespaceTouched( namespace )

Returns a boolean, true if one or more fields in the namespace are touched.

useNamespaceIsValid( namespace )
isNamespaceValid( namespace )

Returns a boolean, true if all fields in the namespace are valid.

useNamespaceValues( namespace )
getNamespaceValues( namespace )

Returns an object with the state value of every field in the namespace.

useNamespaceValuesOnFocus( namespace )
getNamespaceValuesOnFocus( namespace )

Returns an object with the valueOnFocus value for every field in the namespace. Notice that only one field will have a value and all others are null, because only one field can have focus.

clearField( namespace, fieldName )
Reset field, but ignoring default value.
See also resetField

resetField( namespace, fieldName )
Reset field to default state and setting last provided default value, if applicable.
See also clearField

clearNamespace( namespace )
Reset namespace, but ignoring the default values of the fields
See also resetNamespace

resetNamespace( namespace )
Reset namespace to default state and setting last provided default value per field
See also clearNamespace

Examples

Basic usages

const Input = (props) => <input {...useConnectField(props, ['dirty', 'touched', 'valid', 'error', 'focus'])} />

const BasicForm = () => (
  <form>
    <Input namespace="basic" fieldName="username" type="text" />
    <Input namespace="basic" fieldName="password" type="password" />
  </form>
)

Recommended usages

const Input = (props) => {
  const { dirty, touched, valid, error, focus, ...fieldProps } = useConnectField(props)

  // just a simple example how you can style the input based on the props, there are better ways to do this
  const inputStyle = useMemo(() => {
    const style = {}
    if (touched) {
      style.borderColor = valid ? 'green' : 'red'
    }
    if (focus) {
      style.backgroundColor = 'lightgrey'
    }

    return style
  }, [focus, touched, valid])

  return (
    <>
      <input {...fieldProps} style={inputStyle} />
      {error && touched && dirty && <span>{error}</span>}
    </>
  )
}

const simpleTextLengthValidator = (value) =>
  value && value.length > 2 && value.length < 10 ? null : 'Text should be between 2 en 10 chars'

const simplePasswordValidator = (value) => (value && /\d/.test(value) ? null : 'Password should contain a number')

const RecommendedForm = () => (
  <form>
    <Input namespace="recommended" fieldName="username" type="text" validator={simpleTextLengthValidator} />
    <Input namespace="recommended" fieldName="password" type="password" validator={simplePasswordValidator} />
  </form>
)

Advanced usages

// see recommended example
const Input = (props) => {
  ...
  return (
    <>
      <input {...fieldProps} style={inputStyle} />
      {error?.length && touched && dirty && error.map((_error) => <span>{_error}</span>}
    </>
  )
}

const textValidator = (value, props) => {
  const errors = []
  if (!value && props.required) {
    errors.push('This field is required')
  }

  if (value && props.required && props.minLength > 0 && value.length < props.minLength) {
    errors.push('Too short')
  }

  if (value && props.maxLength > 0 && value.length > props.maxLength) {
    errors.push('Too long')
  }

  return errors.length ? errors : null
}

const passwordValidator = (value, props) => {
  const errors = []

  if (value && props.minLength > 0 && value.length < props.minLength) {
    errors.push('Too short')
  }

  if (value && props.containNumbers && !/\d/.test(value)) {
    errors.push('Should contain at least one number')
  }

  if (value && props.containUpperCase && !/[A-Z]/.test(value)) {
    errors.push('Should contain at least upper case char')
  }

  return errors
}

const UsernameInput = () => {
  // Additional actions (these are executed after field state is updated).
  const handleFocus = useCallback((event) => {...}, [...])
  const handleChange = useCallback((event) => {...}, [...])
  const handleBlur = useCallback((event) => {...}, [...])

  return (
    <Input
      namespace="advanced"
      fieldName="username"
      type="text"
      defaultValue="fooBar"
      required
      minLength={2}
      maxLength={15}
      validator={textValidator}
      onFocus={handleFocus}
      onChange={handleChange}
      onBlur={handleBlur}
    />
  )
}

const NicknameInput = () => (
  <Input
    namespace="advanced"
    fieldName="nickname"
    type="text"
    defaultValue="barFoo"
    minLength={5}
    maxLength={25}
    validator={textValidator}
  />
)

const PasswordInput = () => (
  <Input
    namespace="advanced"
    fieldName="password"
    type="password"
    required
    minLength={8}
    containNumbers
    validator={passwordValidator}
  />
)

const SubmitButton = () => <button type="submit" disabled={!useNamespaceIsValid('advanced')} />

const Form = () => <form style={{ backgroundColor: useNamespaceHasFocus('advanced') ? 'lightblue' : 'inherit' }} />

const AdvancedForm = () => (
  <Form>
    <UsernameInput />
    <NicknameInput />
    <PasswordInput />
    <SubmitButton />
  </Form>
)