Last active
June 14, 2019 22:04
-
-
Save hpneo/0bba281f7dd7e7ace08e37606729b42e to your computer and use it in GitHub Desktop.
PoC Forms in Preact using Higher-Order Components
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { h, cloneElement, Component } from 'preact'; | |
import linkState from 'linkstate'; | |
const injectFormProps = (child, { component }) => { | |
const childChildren = (child.children || []).map(childrenChild => injectFormProps(childrenChild, { component })); | |
if (child.nodeName === 'form') { | |
const childAttributes = { | |
...child.attributes, | |
onSubmit(event) { | |
event.preventDefault(); | |
if (child.attributes.onSubmit) { | |
child.attributes.onSubmit(component.state); | |
} | |
} | |
}; | |
return cloneElement(child, childAttributes, childChildren); | |
} | |
if (child.nodeName === 'input') { | |
const childAttributes = { | |
...child.attributes, | |
onInput(event) { | |
linkState(component, event.target.name, 'target.value')(event); | |
} | |
}; | |
child.attributes = childAttributes; | |
return cloneElement(child, childAttributes, childChildren); | |
} | |
return child; | |
}; | |
export const withForm = (options = {}) => { | |
if (!options.name) { | |
throw new Error('Form name required'); | |
} | |
return (WrappedComponent) => | |
class WithForm extends Component { | |
render(props, state) { | |
let children = WrappedComponent({ ...props, form: { data: state } }); | |
if (!Array.isArray(children)) { | |
children = [children]; | |
} | |
return children.map(child => injectFormProps(child, { component: this }))[0]; | |
} | |
}; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { h, cloneElement } from 'preact'; | |
const store = {}; | |
const injectFormProps = (child, formStore) => { | |
const childChildren = (child.children || []).map(childrenChild => injectFormProps(childrenChild, formStore)); | |
if (child.nodeName === 'form') { | |
const childAttributes = { | |
...child.attributes, | |
onSubmit(event) { | |
event.preventDefault(); | |
if (child.attributes.onSubmit) { | |
child.attributes.onSubmit(formStore); | |
} | |
} | |
}; | |
return cloneElement(child, childAttributes, childChildren); | |
} | |
if (child.nodeName === 'input') { | |
const childAttributes = { | |
...child.attributes, | |
onInput(event) { | |
formStore[event.target.name] = event.target.value; | |
} | |
}; | |
child.attributes = childAttributes; | |
return cloneElement(child, childAttributes, childChildren); | |
} | |
return child; | |
}; | |
export const withForm = (options = {}) => { | |
if (!options.name) { | |
throw new Error('Form name required'); | |
} | |
store[options.name] = {}; | |
return (WrappedComponent) => | |
(props) => { | |
let children = WrappedComponent({ ...props, form: {} }); | |
if (!Array.isArray(children)) { | |
children = [children]; | |
} | |
return children.map(child => injectFormProps(child, store[options.name]))[0]; | |
}; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const LoginForm = ({ form = {} }) => { | |
const { pristine, submitting, isValid, errors } = form; | |
return ( | |
// eslint-disable-next-line no-console | |
<form onSubmit={(data) => { isValid ? console.log(data) : console.error(data, errors); }}> | |
<p> | |
<input type="text" name="username" /> | |
</p> | |
<p> | |
<input type="password" name="password" /> | |
</p> | |
<p> | |
<button type="submit" disabled={pristine || submitting}>Submit</button> | |
</p> | |
<p> | |
<input type="checkbox" name="remember_me" /> | |
</p> | |
</form> | |
); | |
}; | |
const ControlledLoginForm = withForm({ name: 'login' })(LoginForm); | |
const Home = () => ( | |
<div class={style.home}> | |
<h1>Home</h1> | |
<p>This is the Home component.</p> | |
<ControlledLoginForm /> | |
</div> | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment