-
-
Notifications
You must be signed in to change notification settings - Fork 245
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
make it more easy to replace components without requiring to implement a custom AutoField #640
Comments
Hi, @macrozone. I'm somewhat against it, as I don't like the idea of modifying it by reference. The broader problem is that In the future (v3), it'll be easier to extend the context (and typing it!), so maybe the |
@radekmie i don't understand the reasoning at all, but let's see what v3 brings Edit: its not modifying by reference, you just can create your extended AutoField which can render a different component on some field-types and pass that custom field to your AutoForm. Creating a custom theme for that is just overkill (at least with the current api). And since the AutoForm defines the AutoField anyway, you don't have that cycle problem. |
You can easily create a custom |
ah yes, that's make it more clear. And in v3 the fields will come from the context? |
Not sure if they will, but it'll be easier to do so, probably even in the project scope (no core change will be needed). |
We'd love something like this as well, we have pretty much rewritten all the uniforms-material components at this point, sometimes just to change one or two lines. If there was a way to at least only rewrite components once, and then have them used by all recursive calls would be great. Maybe an API like this could be helpful: import { makeAutoField, FIELD_TYPES } from 'uniforms';
const AutoField = makeAutoField({
getComponent: type => {
if (type === FIELD_TYPES.NESTED) {
return CustomListField;
}
if (type === FIELD_TYPES.SELECT) {
// Could address #642
return (props) => <CustomSelectField {...props} options={[{ value: null, label: 'Pick one' }, ...props.options]} />;
}
if (type === FIELD_TYPES.RADIO) {
// Merge these props in
return { buttonColor: 'red' }
}
if (type === FIELD_TYPES.LIST) {
// Allow adding stuff in the render method, keep it modular
return { children: <><hr /><h2>This is a list field</h2></> }
}
// Return undefined to get the original included component
}
}) |
I've been thinking about it quite much lately. After experimenting with a few approaches, here are my thoughts:
Currently, I think that making the // Option A
export default function AutoField(props: AutoFieldProps) {
const AutoFieldComponent = useContext(AutoFieldContext);
return <AutoFieldComponent {...props} />;
}
// Option B
export default function AutoField(props: AutoFieldProps) {
const renderer = useContext(AutoFieldContext);
return renderer(props);
} (default implementation is set as a context default) Upsides? Works out of the box, is 100% configurable and is backward compatible. Downsides? It creates an additional context. It shouldn't be a problem though, as it's probably not going to change often (if at all). I'm just not sure if it's better to store an actual component in the context (option A) or a render function (option B). The former seems cleaner (simply provide a component), but the latter has one advantage - it creates no intermediate components (it'd look just like right now |
Somewhat related to this since I found myself in a similar position: I still find it a bit weird that there are now two places which determine the correct component. Having an AutoField feels logical, but setting the correct component from the bridge based on bridge specific fields (type and format in case of json schema) also makes sense.. BTW: the current bridge design is a bit cumbersome for this since you need |
Hi @hmvp. It's not really two places - it's the |
I can relate to this feedback. It is really easy to get some basic forms but when requirements changing it is hard to customize them without changing underlying UI packages. |
I think I've found some middle ground that may work the best: export default function AutoField(originalProps: AutoFieldProps) {
const props = useField(originalProps.name, originalProps)[0];
- let { component } = props;
+ const detect = useContext(AutoFieldContext);
+ let { component = detect(props) } = props;
// Nothing changes below.
} This means that the function that defines the component will receive all field props and acts only as a fallback (i.e. props > schema > detection). I think that leaving the current detection logic in is a good idea - otherwise one would have to use it directly in order to add one more rule. |
If you just want to replace one field (e.g. replace the ListField with a custom one for all schemas), you need to reimplement the whole AutoField.
It would probably easier, if you could pass a
getFieldComponent
to a AutoForm, which would hook into here: https://github.com/vazco/uniforms/blob/master/packages/uniforms-material/src/AutoField.tsx#L24 and would takeprops
and could return a Component. If no component is returned, it would fallback to the normal logic.The text was updated successfully, but these errors were encountered: