
Security News
Software Engineering Daily Podcast: Feross on AI, Open Source, and Supply Chain Risk
Socket CEO Feross Aboukhadijeh joins Software Engineering Daily to discuss modern software supply chain attacks and rising AI-driven security risks.
react-attire
Advanced tools
A dress code for your React forms.
This package contains a component and some utility functions that should make your life easier when dealing with forms with ReactJS. It has no hard dependencies but does obviously require React to work.
npm install --save react-attire
Typings are bundled within the main package, so they should work out of the box!
The Attire component uses a render prop to keep track of a form's internal state, providing both the current value of each form element and a callback to change it.
Attire works by hiding the form's state management behind the Attire component's implementation. You just need to provide the name, value and onChange props for each or your inputs.
Here's a simple example:
import React from 'react'
import { Attire } from 'react-attire'
class MyForm extends React.Component {
render() {
return (
<Attire>
{(data, onChange) => (
<div>
<label>Your name:</label>
<input type="text" name="name" value={data.name} onChange={onChange} />
</div>
)}
</Attire>
)
}
}
There are cases when you won't use regular inputs, but some other, more exotic controls (think of a custom datepicker, for example). In these cases, you can call Attire's onChange callback by explicitly passing both the name and the new value:
import React from 'react'
import { Attire } from 'react-attire'
import { DatePicker } from 'some-datepicker-component'
class MyForm extends React.Component {
render() {
return (
<Attire>
{(data, onChange) => (
<div>
<label>Your birthday:</label>
<DatePicker currentDate={data.birthday} onChange={date => onChange('birthday', date)} />
</div>
)}
</Attire>
)
}
}
This approch also works well when you want to handle subforms that are rendered within their own component and use a callback to notify the parent form of a change in their data, as the second parameter passed to the onChange callback can be of any type (Attire doesn't make any assumption).
🎓 Did you know?
Yes, you can probably use inline functions, the world is not going to explode.
Sometimes it may be useful to change more than one field at once. You can do so by calling the onChange function passing it an object as its only argument; all the object keys will be set/updated on the Attire's instance internal state:
import React from 'react'
import { Attire } from 'react-attire'
class MyForm extends React.Component {
render() {
return (
<Attire>
{(data, onChange) => (
<div>
<label>Your name:</label>
<input
type="text"
name="name"
value={data.name}
onChange={e =>
onChange({
name: e.target.value,
random: Math.random()
})
}
/>
{data.random && <p>The latest random number is {data.random}</p>}
</div>
)}
</Attire>
)
}
}
As with most forms, you'll probably want to initialize it with some values. You can do so by passing an initial prop to the Attire component:
import React from 'react'
import { Attire } from 'react-attire'
class MyForm extends React.Component {
render() {
return (
<Attire initial={{ name: 'Frankie Frankson' }}>
{(data, onChange) => (
<div>
<label>Your name:</label>
<input type="text" name="name" value={data.name} onChange={onChange} />
</div>
)}
</Attire>
)
}
}
If you want to call a function every time something changes in your form, regardless of which field, you can pass a callback to the onChange prop of the Attire component. This is not the same as the onChange callback passed to your render prop! (I might want to change the name of either one at some point, if you have suggestions open an issue):
import React from 'react'
import { Attire } from 'react-attire'
class MyForm extends React.Component {
render() {
return (
<Attire onChange={data => console.log('I sense a disturbance in the Force...', data)}>
{(data, onChange) => (
<div>
<label>Your name:</label>
<input type="text" name="name" value={data.name} onChange={onChange} />
</div>
)}
</Attire>
)
}
}
Let's say you want to include a reset button to clear your form. How would you go about clearing a state that is outside of your control? Attire provides a third argument to your render prop, a reset function you can call to reset the form to its initial state. Calling this function will also trigger the global onChange callback if present.
import React from 'react'
import { Attire } from 'react-attire'
class MyForm extends React.Component {
render() {
return (
<Attire initial={{ name: 'Frankie Frankson' }}>
{(data, onChange, reset) => (
<div>
<label>Your name:</label>
<input type="text" name="name" value={data.name} onChange={onChange} />
<button onClick={reset}>Reset my name!</button>
</div>
)}
</Attire>
)
}
}
Sometimes you might want to react to a change of your initial state (for example when using the same form to edit an element selected from a list). You can pass a callback to the onInitialChange prop, which receives the current form data, the previous initial value and the new initial value as its parameters, and allows you to return a new partial state for the form (or false if you don't want the state to change). This way you can change either the whole state or just a part of it, depending on what your application requirements.
import React from 'react'
import { Attire } from 'react-attire'
const items = [{ name: 'Billy' }, { name: 'Jimmy' }]
class MyForm extends React.Component {
state = {
currentItem: items[0]
}
handleItemSelect = (data, prevItem) => {
return this.state.currentItem
}
render() {
return (
<div>
<button onClick={() => this.setState({ currentItem: items[1] })}>Select another item</button>
<Attire initial={this.state.currentItem} onInitialChange={this.handleItemSelect}>
{(data, onChange, reset) => (
<div>
<label>Your name:</label>
<input type="text" name="name" value={data.name} onChange={onChange} />
</div>
)}
</Attire>
</div>
)
}
}
It's nice to include some kind of client-side validation on your form. Attire makes no assumption on how your data should be validated, but I've included a validate function that you can use to add some validation logic; if you need a more complex solution, you can of course use an external library:
import React from 'react'
import { Attire, validate } from 'react-attire'
const validateMyForm = validate({ name: v => v && v.length > 3 })
class MyForm extends React.Component {
handleFormSubmit = data => {
validateMyForm(data)
.then(() => {
console.log('All good!')
})
.catch(validationStatus => {
console.error('Validation error', validationStatus)
})
}
render() {
return (
<Attire initial={{ name: 'Frankie Frankson' }}>
{(data, onChange, reset) => (
<div>
<label>Your name:</label>
<input type="text" name="name" value={data.name} onChange={onChange} />
<button onClick={() => this.handleFormSubmit(data)}>Go!</button>
</div>
)}
</Attire>
)
}
}
The function accepts an object that defines validation rules for any one of the fields of your form (you can include all of them, or just some) and your data, and returns a Promise which resolves when all rules are fullfilled, rejects otherwise. If it rejects, you'll get a validationStatus object specifying which fields have not passed validation. If you don't pass your data right away, you'll get a curried function that you'll be able to call later with your rules already set.
Much in the same spirit of validation, form data transformation can be useful whenever you want the data your user has entered to be transformed before being used. While you can do this manually, I've included a transform function that you can use:
import React from 'react'
import { Attire, validate, transform } from 'react-attire'
const validateMyForm = validate({ name: v => v && v.length > 3 })
const transformMyForm = transform({ name: v => v && v.toLowerCase() })
class MyForm extends React.Component {
handleFormSubmit = data => {
const transformedData = transformMyForm(data)
validateMyForm(transformedData)
.then(() => {
console.log('All good!')
})
.catch(validationStatus => {
console.error('Validation error', validationStatus)
})
}
render() {
return (
<Attire initial={{ name: 'Frankie Frankson' }}>
{(data, onChange, reset) => (
<div>
<label>Your name:</label>
<input type="text" name="name" value={data.name} onChange={onChange} />
<button onClick={() => this.handleFormSubmit(data)}>Go!</button>
</div>
)}
</Attire>
)
}
}
If some of your input values are undefined, React will complain that your inputs are changing from being uncontrolled to being controlled. You can avoid this by simply using an initial value or by using the || notation:
<input value={data.value || ''} />
Since this is ugly to see, I have provided a value function that does this for you:
import React from 'react'
import { Attire, value } from 'react-attire'
class MyForm extends React.Component {
render() {
return (
<Attire>
{(data, onChange) => (
<div>
<label>Your name:</label>
<input type="text" name="name" value={value(data.name)} onChange={onChange} />
</div>
)}
</Attire>
)
}
}
That's about it! :)
This was mostly a PoC I've made to see how render props fit in the "form" use case. If you see something you don't like or think that something is broken, please open an issue or better yet, make a PR!
🍻 If you use this and you like it, consider buying me a beer by sending BTC to 3NFMFZrWGszYThP3MQsuPgJc2S8R6WinJe :)
MIT
FAQs
A dress code for your React forms
The npm package react-attire receives a total of 3 weekly downloads. As such, react-attire popularity was classified as not popular.
We found that react-attire demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Socket CEO Feross Aboukhadijeh joins Software Engineering Daily to discuss modern software supply chain attacks and rising AI-driven security risks.

Security News
GitHub has revoked npm classic tokens for publishing; maintainers must migrate, but OpenJS warns OIDC trusted publishing still has risky gaps for critical projects.

Security News
Rust’s crates.io team is advancing an RFC to add a Security tab that surfaces RustSec vulnerability and unsoundness advisories directly on crate pages.