uiFunction<TProps extends UiProps> function
UiFactory<TProps>
uiFunction<TProps extends UiProps>(
- dynamic functionComponent(
- TProps props
- dynamic _config
Declares a function component and returns a factory that can be used to render it.
_config
should always be a UiFactoryConfig<TProps>
and is only dynamic
to
avoid an unnecessary cast in the boilerplate.
Example:
UiFactory<FooProps> Foo = uiFunction(
(props) {
// Set default props using null-aware operators.
final isDisabled = props.isDisabled ?? false;
final items = props.items ?? [];
// Return the rendered component contents here.
return Fragment()(
Dom.div()(items),
(Dom.button()..disabled = isDisabled)('Click me!'),
);
},
// The generated props config will match the factory name.
_$FooConfig, // ignore: undefined_identifier
);
// Multiple function components can be declared with the same props.
UiFactory<FooProps> AnotherFoo = uiFunction(
(props) {
return (Foo()
..items = props.items
..isDisabled = true
)();
},
_$AnotherFooConfig, // ignore: undefined_identifier
);
mixin FooProps on UiProps {
bool isDisabled;
Iterable<String> items;
}
OR Optionally pass in an existing PropsFactory in place of a props _config
.
UiFactory<FooProps> Bar = uiFunction(
(props) {
return (Dom.button()..disabled = props.isDisabled)('Click me!');
},
UiFactoryConfig(
propsFactory: PropsFactory.fromUiFactory(Foo),
displayName: 'Bar',
),
);
OR Don't set propsFactory
when using UiProps
, as a generic one will be
created for the component in uiFunction.
UiFactory<UiProps> Foo = uiFunction(
(props) {
return Dom.div()('prop id: ${props.id}');
},
UiFactoryConfig(
displayName: 'Foo',
),
);
Learn more: reactjs.org/docs/components-and-props.html#function-and-class-components.
Implementation
// TODO: right now only top level factory declarations will generate props configs.
UiFactory<TProps> uiFunction<TProps extends UiProps>(
dynamic Function(TProps props) functionComponent,
dynamic _config,
) {
ArgumentError.checkNotNull(_config, '_config');
if (_config is! UiFactoryConfig<TProps>) {
throw ArgumentError('_config should be a UiFactory<TProps>. Make sure you are '
r'using either the generated factory config (i.e. _$FooConfig) or manually '
'declaring your config correctly.');
}
final config = _config;
var propsFactory = config.propsFactory;
// Get the display name from the inner function if possible so it doesn't become `_uiFunctionWrapper`
final displayName = config.displayName ?? getFunctionName(functionComponent);
dynamic _uiFunctionWrapper(JsBackedMap props) {
return functionComponent(propsFactory!.jsMap(props));
}
final factory = react.registerFunctionComponent(
_uiFunctionWrapper,
displayName: displayName,
);
if (propsFactory == null) {
if (TProps != UiProps && TProps != GenericUiProps) {
throw ArgumentError(
'config.propsFactory must be provided when using custom props classes');
}
propsFactory = PropsFactory.fromUiFactory(
([backingMap]) => GenericUiProps(factory, backingMap))
as PropsFactory<TProps>;
}
// Work around propsFactory not getting promoted to non-nullable in _uiFactory: https://github.com/dart-lang/language/issues/1536
final nonNullablePropsFactory = propsFactory;
TProps _uiFactory([Map? backingMap]) {
TProps builder;
if (backingMap == null) {
// propsFactory should get promoted to non-nullable here, but it does not some reason propsF
builder = nonNullablePropsFactory.jsMap(JsBackedMap());
} else if (backingMap is JsBackedMap) {
builder = nonNullablePropsFactory.jsMap(backingMap);
} else {
builder = nonNullablePropsFactory.map(backingMap);
}
return builder..componentFactory = factory;
}
registerComponentTypeAlias(factory, _uiFactory);
return _uiFactory;
}