{"id":13481290,"url":"https://github.com/gianmarcotoso/react-attire","last_synced_at":"2026-02-05T17:32:23.093Z","repository":{"id":67911122,"uuid":"106260895","full_name":"gianmarcotoso/react-attire","owner":"gianmarcotoso","description":"A dress code for your React forms","archived":false,"fork":false,"pushed_at":"2018-11-03T15:49:12.000Z","size":273,"stargazers_count":24,"open_issues_count":1,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-10-02T14:58:02.195Z","etag":null,"topics":["forms","react"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gianmarcotoso.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-10-09T09:07:07.000Z","updated_at":"2023-04-02T09:48:47.000Z","dependencies_parsed_at":"2023-02-21T06:31:08.964Z","dependency_job_id":null,"html_url":"https://github.com/gianmarcotoso/react-attire","commit_stats":{"total_commits":52,"total_committers":1,"mean_commits":52.0,"dds":0.0,"last_synced_commit":"d4a069a123dbfbf63b3d0f6d1c18a527cc760b7f"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/gianmarcotoso/react-attire","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gianmarcotoso%2Freact-attire","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gianmarcotoso%2Freact-attire/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gianmarcotoso%2Freact-attire/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gianmarcotoso%2Freact-attire/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gianmarcotoso","download_url":"https://codeload.github.com/gianmarcotoso/react-attire/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gianmarcotoso%2Freact-attire/sbom","scorecard":{"id":426102,"data":{"date":"2025-08-11","repo":{"name":"github.com/gianmarcotoso/react-attire","commit":"d4a069a123dbfbf63b3d0f6d1c18a527cc760b7f"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.7,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"85 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-6chw-6frg-f759","Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-fwr7-v2mv-hh25","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-cwfw-4gq5-mrqx","Warn: Project is vulnerable to: GHSA-g95f-p29q-9xw4","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-x9w5-v3q2-3rhw","Warn: Project is vulnerable to: GHSA-w8qv-6jwh-64r5","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-p28h-cc7q-c4fg","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-vh7m-p724-62c2","Warn: Project is vulnerable to: GHSA-r9p9-mrjm-926w","Warn: Project is vulnerable to: GHSA-434g-2637-qmqr","Warn: Project is vulnerable to: GHSA-49q7-c7j4-3p7m","Warn: Project is vulnerable to: GHSA-977x-g7h5-7qgw","Warn: Project is vulnerable to: GHSA-f7q4-pwc6-w24p","Warn: Project is vulnerable to: GHSA-fc9h-whq2-v747","Warn: Project is vulnerable to: GHSA-vjh7-7g9h-fjfh","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-8r6j-v8pm-fqw3","Warn: Project is vulnerable to: MAL-2023-462","Warn: Project is vulnerable to: GHSA-vvj3-85vf-fgmw","Warn: Project is vulnerable to: GHSA-q42p-pg8m-cqh6","Warn: Project is vulnerable to: GHSA-w457-6q6x-cgp9","Warn: Project is vulnerable to: GHSA-62gr-4qp9-h98f","Warn: Project is vulnerable to: GHSA-f52g-6jhx-586p","Warn: Project is vulnerable to: GHSA-2cf5-4w76-r9qv","Warn: Project is vulnerable to: GHSA-3cqr-58rm-57f8","Warn: Project is vulnerable to: GHSA-g9r4-xpmj-mj65","Warn: Project is vulnerable to: GHSA-q2c6-c6pm-g3gh","Warn: Project is vulnerable to: GHSA-765h-qjxv-5f44","Warn: Project is vulnerable to: GHSA-f2jv-r9rf-7988","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-qqgx-2p2h-9c37","Warn: Project is vulnerable to: GHSA-2pr6-76vf-7546","Warn: Project is vulnerable to: GHSA-8j8c-7jfh-h6hx","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-6c8f-qphg-qjgp","Warn: Project is vulnerable to: GHSA-76p3-8jx3-jpfq","Warn: Project is vulnerable to: GHSA-3rfm-jhwj-7488","Warn: Project is vulnerable to: GHSA-hhq3-ff78-jv3g","Warn: Project is vulnerable to: GHSA-jf85-cpcp-j695","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-4xcv-9jjx-gfj3","Warn: Project is vulnerable to: GHSA-7wpw-2hjm-89gp","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-fhjf-83wg-r2j9","Warn: Project is vulnerable to: GHSA-5fw9-fq32-wv5p","Warn: Project is vulnerable to: GHSA-rp65-9cf3-cjxr","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-h7cp-r72f-jxh6","Warn: Project is vulnerable to: GHSA-v62p-rq8g-8h59","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-h9rv-jmmf-4pgx","Warn: Project is vulnerable to: GHSA-hxcc-f52p-wc94","Warn: Project is vulnerable to: GHSA-4g88-fppr-53pp","Warn: Project is vulnerable to: GHSA-4jqc-8m5r-9rpr","Warn: Project is vulnerable to: GHSA-vx3p-948g-6vhq","Warn: Project is vulnerable to: GHSA-3jfq-g458-7qm9","Warn: Project is vulnerable to: GHSA-r628-mhmh-qjhw","Warn: Project is vulnerable to: GHSA-9r2w-394v-53qc","Warn: Project is vulnerable to: GHSA-5955-9wpr-37jh","Warn: Project is vulnerable to: GHSA-qq89-hq3f-393p","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36","Warn: Project is vulnerable to: GHSA-jgrx-mgxx-jf9v","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-cf4h-3jhx-xvhq","Warn: Project is vulnerable to: GHSA-6fc8-4gx4-v693","Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q","Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh","Warn: Project is vulnerable to: GHSA-p9pc-299p-vxgp"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-19T02:17:21.338Z","repository_id":67911122,"created_at":"2025-08-19T02:17:21.338Z","updated_at":"2025-08-19T02:17:21.338Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29127062,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-05T17:12:17.649Z","status":"ssl_error","status_checked_at":"2026-02-05T17:11:23.670Z","response_time":65,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["forms","react"],"created_at":"2024-07-31T17:00:50.506Z","updated_at":"2026-02-05T17:32:23.088Z","avatar_url":"https://github.com/gianmarcotoso.png","language":"TypeScript","funding_links":[],"categories":["Components"],"sub_categories":["Forms"],"readme":"[![Build Status](https://travis-ci.org/gianmarcotoso/react-attire.svg?branch=master)](https://travis-ci.org/gianmarcotoso/react-attire)\n[![npm version](https://badge.fury.io/js/react-attire.svg)](https://badge.fury.io/js/react-attire)\n\n# Attire\n\nA dress code for your React forms.\n\nThis 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.\n\n## Installation\n\n```\nnpm install --save react-attire\n```\n\nTypings are bundled within the main package, so they should work out of the box!\n\n## How does it work?\n\nThe `Attire` component uses a [render prop](https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce) to keep track of a form's internal state, providing both the current value of each form element and a callback to change it.\n\n### Basic Usage\n\nAttire 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.\n\nHere's a simple example:\n\n```jsx\nimport React from 'react'\nimport { Attire } from 'react-attire'\n\nclass MyForm extends React.Component {\n\trender() {\n\t\treturn (\n\t\t\t\u003cAttire\u003e\n\t\t\t\t{(data, onChange) =\u003e (\n\t\t\t\t\t\u003cdiv\u003e\n\t\t\t\t\t\t\u003clabel\u003eYour name:\u003c/label\u003e\n\t\t\t\t\t\t\u003cinput type=\"text\" name=\"name\" value={data.name} onChange={onChange} /\u003e\n\t\t\t\t\t\u003c/div\u003e\n\t\t\t\t)}\n\t\t\t\u003c/Attire\u003e\n\t\t)\n\t}\n}\n```\n\n### Custom form inputs\n\nThere are cases when you won't use regular `input`s, 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:\n\n```jsx\nimport React from 'react'\nimport { Attire } from 'react-attire'\nimport { DatePicker } from 'some-datepicker-component'\n\nclass MyForm extends React.Component {\n\trender() {\n\t\treturn (\n\t\t\t\u003cAttire\u003e\n\t\t\t\t{(data, onChange) =\u003e (\n\t\t\t\t\t\u003cdiv\u003e\n\t\t\t\t\t\t\u003clabel\u003eYour birthday:\u003c/label\u003e\n\t\t\t\t\t\t\u003cDatePicker currentDate={data.birthday} onChange={date =\u003e onChange('birthday', date)} /\u003e\n\t\t\t\t\t\u003c/div\u003e\n\t\t\t\t)}\n\t\t\t\u003c/Attire\u003e\n\t\t)\n\t}\n}\n```\n\nThis 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).\n\n\u003e 🎓 **Did you know?**\n\u003e\n\u003e Yes, you can probably use inline functions, [the world is not going to explode](https://cdb.reacttraining.com/react-inline-functions-and-performance-bdff784f5578).\n\n### Changing multiple fields at once\n\nSometimes 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:\n\n```jsx\nimport React from 'react'\nimport { Attire } from 'react-attire'\n\nclass MyForm extends React.Component {\n\trender() {\n\t\treturn (\n\t\t\t\u003cAttire\u003e\n\t\t\t\t{(data, onChange) =\u003e (\n\t\t\t\t\t\u003cdiv\u003e\n\t\t\t\t\t\t\u003clabel\u003eYour name:\u003c/label\u003e\n\t\t\t\t\t\t\u003cinput\n\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\tname=\"name\"\n\t\t\t\t\t\t\tvalue={data.name}\n\t\t\t\t\t\t\tonChange={e =\u003e\n\t\t\t\t\t\t\t\tonChange({\n\t\t\t\t\t\t\t\t\tname: e.target.value,\n\t\t\t\t\t\t\t\t\trandom: Math.random()\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t/\u003e\n\t\t\t\t\t\t{data.random \u0026\u0026 \u003cp\u003eThe latest random number is {data.random}\u003c/p\u003e}\n\t\t\t\t\t\u003c/div\u003e\n\t\t\t\t)}\n\t\t\t\u003c/Attire\u003e\n\t\t)\n\t}\n}\n```\n\n### Setting the initial state\n\nAs 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:\n\n```jsx\nimport React from 'react'\nimport { Attire } from 'react-attire'\n\nclass MyForm extends React.Component {\n\trender() {\n\t\treturn (\n\t\t\t\u003cAttire initial={{ name: 'Frankie Frankson' }}\u003e\n\t\t\t\t{(data, onChange) =\u003e (\n\t\t\t\t\t\u003cdiv\u003e\n\t\t\t\t\t\t\u003clabel\u003eYour name:\u003c/label\u003e\n\t\t\t\t\t\t\u003cinput type=\"text\" name=\"name\" value={data.name} onChange={onChange} /\u003e\n\t\t\t\t\t\u003c/div\u003e\n\t\t\t\t)}\n\t\t\t\u003c/Attire\u003e\n\t\t)\n\t}\n}\n```\n\n### Global change notification\n\nIf 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):\n\n```jsx\nimport React from 'react'\nimport { Attire } from 'react-attire'\n\nclass MyForm extends React.Component {\n\trender() {\n\t\treturn (\n\t\t\t\u003cAttire onChange={data =\u003e console.log('I sense a disturbance in the Force...', data)}\u003e\n\t\t\t\t{(data, onChange) =\u003e (\n\t\t\t\t\t\u003cdiv\u003e\n\t\t\t\t\t\t\u003clabel\u003eYour name:\u003c/label\u003e\n\t\t\t\t\t\t\u003cinput type=\"text\" name=\"name\" value={data.name} onChange={onChange} /\u003e\n\t\t\t\t\t\u003c/div\u003e\n\t\t\t\t)}\n\t\t\t\u003c/Attire\u003e\n\t\t)\n\t}\n}\n```\n\n### Resetting the state\n\nLet'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.\n\n```jsx\nimport React from 'react'\nimport { Attire } from 'react-attire'\n\nclass MyForm extends React.Component {\n\trender() {\n\t\treturn (\n\t\t\t\u003cAttire initial={{ name: 'Frankie Frankson' }}\u003e\n\t\t\t\t{(data, onChange, reset) =\u003e (\n\t\t\t\t\t\u003cdiv\u003e\n\t\t\t\t\t\t\u003clabel\u003eYour name:\u003c/label\u003e\n\t\t\t\t\t\t\u003cinput type=\"text\" name=\"name\" value={data.name} onChange={onChange} /\u003e\n\n\t\t\t\t\t\t\u003cbutton onClick={reset}\u003eReset my name!\u003c/button\u003e\n\t\t\t\t\t\u003c/div\u003e\n\t\t\t\t)}\n\t\t\t\u003c/Attire\u003e\n\t\t)\n\t}\n}\n```\n\n### Handling initial data change\n\nSometimes 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 state for the form (or `false` if you don't want the state to change). The previous state will be **completely** replaced be the one returned by this callback.\n\n```jsx\nimport React from 'react'\nimport { Attire } from 'react-attire'\n\nconst items = [{ name: 'Billy' }, { name: 'Jimmy' }]\n\nclass MyForm extends React.Component {\n\tstate = {\n\t\tcurrentItem: items[0]\n\t}\n\n\thandleItemSelect = (data, prevItem) =\u003e {\n\t\treturn this.state.currentItem\n\t}\n\n\trender() {\n\t\treturn (\n\t\t\t\u003cdiv\u003e\n\t\t\t\t\u003cbutton onClick={() =\u003e this.setState({ currentItem: items[1] })}\u003eSelect another item\u003c/button\u003e\n\t\t\t\t\u003cAttire initial={this.state.currentItem} onInitialChange={this.handleItemSelect}\u003e\n\t\t\t\t\t{(data, onChange, reset) =\u003e (\n\t\t\t\t\t\t\u003cdiv\u003e\n\t\t\t\t\t\t\t\u003clabel\u003eYour name:\u003c/label\u003e\n\t\t\t\t\t\t\t\u003cinput type=\"text\" name=\"name\" value={data.name} onChange={onChange} /\u003e\n\t\t\t\t\t\t\u003c/div\u003e\n\t\t\t\t\t)}\n\t\t\t\t\u003c/Attire\u003e\n\t\t\t\u003c/div\u003e\n\t\t)\n\t}\n}\n```\n\n### Form Validation\n\nIt'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:\n\n```jsx\nimport React from 'react'\nimport { Attire, validate } from 'react-attire'\n\nconst validateMyForm = validate({ name: v =\u003e v \u0026\u0026 v.length \u003e 3 })\n\nclass MyForm extends React.Component {\n\thandleFormSubmit = data =\u003e {\n\t\tvalidateMyForm(data)\n\t\t\t.then(() =\u003e {\n\t\t\t\tconsole.log('All good!')\n\t\t\t})\n\t\t\t.catch(validationStatus =\u003e {\n\t\t\t\tconsole.error('Validation error', validationStatus)\n\t\t\t})\n\t}\n\n\trender() {\n\t\treturn (\n\t\t\t\u003cAttire initial={{ name: 'Frankie Frankson' }}\u003e\n\t\t\t\t{(data, onChange, reset) =\u003e (\n\t\t\t\t\t\u003cdiv\u003e\n\t\t\t\t\t\t\u003clabel\u003eYour name:\u003c/label\u003e\n\t\t\t\t\t\t\u003cinput type=\"text\" name=\"name\" value={data.name} onChange={onChange} /\u003e\n\n\t\t\t\t\t\t\u003cbutton onClick={() =\u003e this.handleFormSubmit(data)}\u003eGo!\u003c/button\u003e\n\t\t\t\t\t\u003c/div\u003e\n\t\t\t\t)}\n\t\t\t\u003c/Attire\u003e\n\t\t)\n\t}\n}\n```\n\nThe 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.\n\n### Form Data Transformation\n\nMuch 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:\n\n```jsx\nimport React from 'react'\nimport { Attire, validate, transform } from 'react-attire'\n\nconst validateMyForm = validate({ name: v =\u003e v \u0026\u0026 v.length \u003e 3 })\nconst transformMyForm = transform({ name: v =\u003e v \u0026\u0026 v.toLowerCase() })\n\nclass MyForm extends React.Component {\n\thandleFormSubmit = data =\u003e {\n\t\tconst transformedData = transformMyForm(data)\n\n\t\tvalidateMyForm(transformedData)\n\t\t\t.then(() =\u003e {\n\t\t\t\tconsole.log('All good!')\n\t\t\t})\n\t\t\t.catch(validationStatus =\u003e {\n\t\t\t\tconsole.error('Validation error', validationStatus)\n\t\t\t})\n\t}\n\n\trender() {\n\t\treturn (\n\t\t\t\u003cAttire initial={{ name: 'Frankie Frankson' }}\u003e\n\t\t\t\t{(data, onChange, reset) =\u003e (\n\t\t\t\t\t\u003cdiv\u003e\n\t\t\t\t\t\t\u003clabel\u003eYour name:\u003c/label\u003e\n\t\t\t\t\t\t\u003cinput type=\"text\" name=\"name\" value={data.name} onChange={onChange} /\u003e\n\n\t\t\t\t\t\t\u003cbutton onClick={() =\u003e this.handleFormSubmit(data)}\u003eGo!\u003c/button\u003e\n\t\t\t\t\t\u003c/div\u003e\n\t\t\t\t)}\n\t\t\t\u003c/Attire\u003e\n\t\t)\n\t}\n}\n```\n\n### \"Uncontrolled input\" warning\n\nIf 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:\n\n```jsx\n\u003cinput value={data.value || ''} /\u003e\n```\n\nSince this is ugly to see, I have provided a `value` function that does this for you:\n\n```jsx\nimport React from 'react'\nimport { Attire, value } from 'react-attire'\n\nclass MyForm extends React.Component {\n\trender() {\n\t\treturn (\n\t\t\t\u003cAttire\u003e\n\t\t\t\t{(data, onChange) =\u003e (\n\t\t\t\t\t\u003cdiv\u003e\n\t\t\t\t\t\t\u003clabel\u003eYour name:\u003c/label\u003e\n\t\t\t\t\t\t\u003cinput type=\"text\" name=\"name\" value={value(data.name)} onChange={onChange} /\u003e\n\t\t\t\t\t\u003c/div\u003e\n\t\t\t\t)}\n\t\t\t\u003c/Attire\u003e\n\t\t)\n\t}\n}\n```\n\nThat's about it! :)\n\n## Contributing\n\nThis 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!\n\n🍻 If you use this and you like it, consider buying me a beer by sending BTC to 3NFMFZrWGszYThP3MQsuPgJc2S8R6WinJe :)\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgianmarcotoso%2Freact-attire","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgianmarcotoso%2Freact-attire","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgianmarcotoso%2Freact-attire/lists"}