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

@schematic-forms/react

v0.9.6

Published

react adapter for schematic forms

Downloads

79

Readme

Description

Schematic forms is schema based form processing library for react writen with typescript.

Instalation

npm i --save @schematic-forms/core @schematic-forms/react

Usage

Library uses context and hooks(or render props) as a main concept of usage.

import React, { FC } from 'react'
import { useController, FormProvider, FieldConsumer } from '@schematic-forms/react'
import { Str } from '@schematic-forms/core'

export const Form: FC<{}> = () => {
    const { controller, submit, clear } = useController({
        fields: {
            email: Str(true),
            password: Str(true)
        },
        validators: {
            email: EmailValidator("INCORRECT_EMAIL")
        },
        submit: (data) => {
            console.log(data)
        }
    })

    return (
        <FormProvider controller={controller} >
            <FieldConsumer field="email">
                {({ value, setValue, error }) => (
                    <div>
                        {error}
                        <input value={value} onChange={e => setValue(e.target.value)} />
                    </div>
                )}
            </FieldConsumer>
            <FieldConsumer field="password">
                {({ value, setValue, error }) => (
                    <div>
                        {error}
                        <input value={value} onChange={e => setValue(e.target.value)} />
                    </div>
                )}
            </FieldConsumer>
            <ErrorConsumer>
                {({ hasError }) => (
                    <button disabled={hasError} onClick={submit} >Submit</button>
                )}
            </ErrorConsumer>
            <button onClick={clear}>Clear</button>
        </FormProvider>
    )
}

What's going on here, yeah? Let me explain. Lets start with useController() hook. it takes config and returns object like { controller, submit, clear }

type config = {
    fields: {
        [key: string]: SchemaType
    },
    validators: {
        [key: string]: (value: FieldValue) => void | Error
    },
    submit: (data: Data) => void | { [key: string]: Error } | Promise<void | { [key: string]: Error }>
}

From top into bottom:

  • fields - form fields. keys - common string, values something like Str(), Bool(), Num(), that u can import from @schematic-forms/core
  • validators - object with field validation functions.
  • submit - function that will be executed when all form conditions are met. submit will get form data and should return void or error map (also it can returns a Promise).

Then we'll take FormProvider component. It's simple context provider requiring controller that useController() returns, no more.

FieldConsumer is dirty(but easy) way to change fields. takes only one prop: field - field name to control. As children takes function with following type

type RenderFunction = (
    props: { value: any, setValue: (nextValue: any
) => void, error: null | string }) => ReactNode

value and setValue react's useState() like functions. error will provide errors on this field

ErrorConsumer represents exception handling.

type ErrorConsumerProps = {
    field?: string
    code?: string
}

If no props were provided ErrorConsumer will react on any exception in any fields. As children u can pass RenderFunction like in example above or just ReactNode.

Value types

Types you can pass into "fields" object.

const fields = {
    str: Str(required: boolean, defaultValue: string),
    num: Num(required: boolean, defaultValue: number),
    object: Obj(
        objectSchema: ObjectSchema, 
        required: boolean, 
        defaultValue: ObjectSchemaRealization
    ),
    enum: Enum(variant: Variant[], required: true, defaultValue: Variant),
    array: Arr(
        arrType: SchemaFieldType, 
        require: boolean, 
        defaultValue: SchemaFieldType[]
    ),
    mix: Mix(TypeToMix[], required: boolean, defaultValue: TypeToMix)
}
// Object type example
const book = Obj({
    title: Str(true),
    count: Num(true),
    author: Obj({
        name: Str(true),
        email: Str(true)
    })
}, true)

// Enum type example
const gender = Arr<["M", "F"]>(["M", "F"], true) 
/* 
You have to use generic argument because typescript identifies arrays like ["M", "F"] as a string array.
*/

// Array type example
const titles = Arr(Str(true), true)

// Mix type example
const mixedType = Mix([
    Str(true),
    Obj({ title: Str(true) }, true)
])

Hooks

useForm

useForm is a right way to change controller fields and create custom form fields.

function useForm<ValueType>(
    name: string, 
    nullValue?: ValueType
): [
    ValueType | undefined, // value
    | (nextVal: ValueType // change function
    | ((prevValue?: ValueType) => ValueType | undefined)) => void,
    string | null // error
]
// Example
const FormInput: FC<{ name: string }> = ({ name }) => {
    const [value, setValue, error] = useForm(name, "");
    
    return (
        <div>
            {error}
            <input 
                value={value} 
                onChenge={e => setValue(e.target.value)} 
            />
        </div>
    )
}
useHasError

Returns true if controller has errors and false if not.

// ... react component with useController hook
const hasErrors = useHasError(controller)
return (
    <div>
        {hasErrors && "ERRORS!"}
        {/* ... */}
    </div>
)
usePending

If your submit function returns Promise you probably want to know it`s status. usePending returns { isPending: true } if promise is pending and { isPending: false } if not.

// ... react component with useController hook
const { isPending } = usePending(controller)
return (
    <div>
        {isPending && "LOADING..."}
        {/* ... */}
    </div>
)
useValue

Returns controller field value

// ... react component with useController hook
const { controller } = useController({
    fields: {
        email: Str(true)
    }
})

const email = useValue(controller, "email")

return (
    <div>
        email: {email}
    </div>
)

Components

ErrorConsumer

Consumes controller errors using context.

interface ErrorConsumerProps {
    children: 
        | ReactNode 
        | ((
            props: { hasError: true, errorCode: string } | { hasError: false, errorCode: null }
        ) => ReactNode)
    
    /** @description observable controller field*/
    field?: string
    
    /** @description error type */
    error?: string
}
  • Without "field" prop component will trigger on any error in any field.
  • "error" prop specify which error type in "field" should trigger re-render.
// Example
// ... react component with useController hook
const { controller } = useController({
    fields: {
        email: Str(true)
    },
    validators: EmailValidator("EMAIL_ERROR")
})

return (
    <FormProvider controller={controller}>
        <ErrorConsumer field={email}>
            Error in E-mail field
        </ErrorConsumer>
        <ErrorConsumer field={email} error="REQUIRED">
            E-mail required
        </ErrorConsumer>
        <ErrorConsumer field={email} error="EMAIL_ERROR">
            E-mail validation failed
        </ErrorConsumer>
        <ErrorConsumer field={email}>
            {({ hasError, errorCode }) => !hasError ? null : (
                <div>
                    {errorCode}
                </div>
            )}
        </ErrorConsumer>
    </FormProvider>
)
PendingConsumer

Keeping the controllers status in check.

// Example
// ... react component with useController hook
const { controller } = useController({
    fields: {
        email: Str(true)
    },
    validators: EmailValidator("EMAIL_ERROR"),
    submit: async () => {
        await delay(5000)
    }
})

return (
    <FormProvider controller={controller}>
        <PendingConsumer>
            Loading...
        </PendingConsumer>
        <PendingConsumer>
            {({ isPending }) => (
                <button disabled={isPending}>
                    submit
                </button>
            )}
        </PendingConsumer>
    </FormProvider>
)