0.1.24 • Published 2 years ago
@dusanjovanov/formy v0.1.24
Formy
Small 📦 Fast - No extra renders ⚡
Install
npm i @dusanjovanov/formy
Create your form class
import { Field } from '@dusanjovanov/formy';
class PersonForm {
firstName = Field(TextField);
lastName = Field(TextField);
fullName = Field(TextField);
init = (context) => {
this.firstName.value = context.person?.firstName ?? '';
this.lastName.value = context.person?.lastName ?? '';
this.fullName.value = context.person
? `${context.person.firstName} ${context.person.lastName}`
: '';
this.firstName.schema = yup.string().required();
};
update = (context, reason) => {
this.firstName.props = {
label: 'First name',
};
this.lastName.props = {
label: 'Last name',
};
this.fullName.props = {
label: 'Full name',
isDisabled: true,
};
this.calculateFullName(reason);
};
calculateFullName = (reason: UpdateReason) => {
if (
reason.type === 'value' &&
(reason.name === 'firstName' || reason.name === 'lastName')
) {
this.fullName.value = `${this.firstName.value} ${this.lastName.value}`;
}
};
}
Explanation
- init - called on mount and when we call
resetForm
- update - called everytime something changes in the fields (props, value, error...)
- update is called with a
reason
argument which tells you what has changed
Create your component
const TextField = ({ label, isVisible, field, background }) => {
if (typeof isVisible === 'boolean' && !isVisible) return null;
return (
<div style={{ background }}>
<h2>{label}</h2>
<input
type="text"
{...field}
value={field.value ?? ''}
onChange={(e) => field.onChange(e.target.value)}
ref={field.focusRef}
/>
{field.error}
</div>
);
};
Explanation
There is a special prop passed to you components called field
.
It includes following things:
- value
- onChange
- onBlur
- error
- focusRef (should be passed to something with a
focus()
method.
Every other prop is arbitrary.
These props are passed with this.fieldName.props = {}
in the class.
Wire everything up
import { Form } from '@dusanjovanov/formy';
const App = () => {
const [person, setPerson] = useState<any>(null);
const formRef = useRef<Form>(null);
useEffect(() => {
(async () => {
const person = await getPerson();
setPerson(person);
})();
}, []);
useEffect(() => {
if (person) {
if (!formRef.current) return;
// when we call resetForm() - init() gets called in the class
formRef.current.resetForm();
}
}, [person]);
const onSubmit = (values, transformedValues) => {
console.log(values, transformedValues);
};
const context = {
person,
};
return (
<div>
<Form
form={PersonForm}
onSubmit={onSubmit}
context={context}
ref={formRef}
>
{({ fields, submitForm, fields, resetForm, focusField }) => {
return (
<>
{fields.firstName}
{fields.lastName}
{fields.fullName}
<br />
<br />
<button onClick={() => submitForm()}>Submit</button>
<button onClick={() => resetForm()}>Reset</button>
</>
);
}}
</Form>
</div>
);
};
Explanation
We're using the Form component and we're passing it 4 things:
form
- Your form classonSubmit
- Function that will be called when you submit if all fields are valid. It will be called with values and transformed values.context
- External dependencies. In this case, it's a person object that we get from the server.ref
- Ref to the Form component. We use this to reset the form from the outside when the person object arrives from the server.