Skip to content

Instantly share code, notes, and snippets.

@hpneo
Last active June 14, 2019 22:04
Show Gist options
  • Save hpneo/0bba281f7dd7e7ace08e37606729b42e to your computer and use it in GitHub Desktop.
Save hpneo/0bba281f7dd7e7ace08e37606729b42e to your computer and use it in GitHub Desktop.
PoC Forms in Preact using Higher-Order Components
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];
}
};
};
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];
};
};
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