Feliz icon indicating copy to clipboard operation
Feliz copied to clipboard

ReactComponent attribute does not work without calling `ofFunction` where used

Open sonicbhoc opened this issue 1 year ago • 14 comments

So I am having a strange issue with Feliz 1.68 and Fable 3.7.1.

I'm trying to author React components. When I just add the [<ReactComponent>] attribute to an F# function with a capital name and a list of properties (which is usually empty), I'm expecting it to generate a React Function Component. But the generated component's logic is essentially comprised of constructing arrays like what's in the F# code. Which is fine, except my React components don't work.

In every example I've seen, all I am supposed to need is the ReactComponent Attribute and supposedly I'd get a function component, and then I can just call that component like a function. But when I do that, I get this error:

Invalid hook call. Hooks can only be called inside of the body of a function component.

And the generated code looks like this:

export function Router() {
    let elements;
    const patternInput = Feliz_React__React_useState_Static_1505(RouterModule_urlSegments(window.location.hash, 1));
    const updateUrl = patternInput[1];
    const currentUrl = patternInput[0];
    return RouterModule_router(createObj(ofArray([["onUrlChanged", updateUrl], (elements = toList(delay(() => {
        const matchValue = Url_Parse_1334CEF1(currentUrl);
        switch (matchValue.tag) {
            case 1: {
                return singleton(createElement("h1", {
                    children: ["Logging In..."],
                }));
            }
            case 0: {
                return singleton(Index());
            }
            case 2: {
                return singleton(Estimator());
            }
            case 3: {
                return singleton(TimeTracker());
            }
            default: {
                return singleton(createElement("h1", {
                    children: ["Error 404: Not Found"],
                }));
            }
        }
    })), ["application", react.createElement(react.Fragment, {}, ...elements)])])));
}

export function PageContainer() {
    let elems_5, elms_1, elms, props_2, elems_2, elms_2, props_6, elems_3;
    const props_9 = ofArray([["className", "is-fullheight"], (elems_5 = [(elms_1 = singleton_1((elms = singleton_1(Navbar()), createElement("div", {
        className: "container",
        children: Interop_reactApi.Children.toArray(Array.from(elms)),
    }))), createElement("div", {
        className: "hero-head",
        children: Interop_reactApi.Children.toArray(Array.from(elms_1)),
    })), (props_2 = singleton_1((elems_2 = [Router()], ["children", Interop_reactApi.Children.toArray(Array.from(elems_2))])), createElement("div", createObj(Helpers_combineClasses("hero-body", props_2)))), (elms_2 = singleton_1((props_6 = ofArray([["className", "is-fluid"], (elems_3 = [createElement("div", createObj(Helpers_combineClasses("content", ofArray([["className", "has-text-centered"], ["children", "© 2022 General Digital Corporation"]]))))], ["children", Interop_reactApi.Children.toArray(Array.from(elems_3))])]), createElement("div", createObj(Helpers_combineClasses("container", props_6))))), createElement("div", {
        className: "hero-foot",
        children: Interop_reactApi.Children.toArray(Array.from(elms_2)),
    }))], ["children", Interop_reactApi.Children.toArray(Array.from(elems_5))])]);
    return createElement("section", createObj(Helpers_combineClasses("hero", props_9)));
}

export function App(pca) {
    const props = ofArray([["instance", pca], ["children", [PageContainer()]]]);
    return Interop_reactApi_1.createElement(msalProvider, createObj(props));
}

render(App(createClient(Msal_config)), document.getElementById("elmish-app"))

In order to get anything to work at all, I have to use the ofFunction function everywhere I use a ReactComponent, which I thought the attribute was supposed to do for me. Then, the generated code looks like this:

export function Router() {
    let elements;
    const patternInput = Feliz_React__React_useState_Static_1505(RouterModule_urlSegments(window.location.hash, 1));
    const updateUrl = patternInput[1];
    const currentUrl = patternInput[0];
    return RouterModule_router(createObj(ofArray([["onUrlChanged", updateUrl], (elements = toList(delay(() => {
        const matchValue = Url_Parse_1334CEF1(currentUrl);
        switch (matchValue.tag) {
            case 1: {
                return singleton(createElement("h1", {
                    children: ["Logging In..."],
                }));
            }
            case 0: {
                return singleton(react.createElement(Index, void 0));
            }
            case 2: {
                return singleton(react.createElement(Estimator, void 0));
            }
            case 3: {
                return singleton(react.createElement(TimeTracker, void 0));
            }
            default: {
                return singleton(createElement("h1", {
                    children: ["Error 404: Not Found"],
                }));
            }
        }
    })), ["application", react.createElement(react.Fragment, {}, ...elements)])])));
}

export function PageContainer() {
    let elems_5, elms_1, elms, props_6, elems_2, elms_2, props_10, elems_3;
    const props_13 = ofArray([["className", "is-fullheight"], (elems_5 = [(elms_1 = singleton_1((elms = singleton_1(react.createElement(Navbar, void 0)), createElement("div", {
        className: "container",
        children: Interop_reactApi.Children.toArray(Array.from(elms)),
    }))), createElement("div", {
        className: "hero-head",
        children: Interop_reactApi.Children.toArray(Array.from(elms_1)),
    })), (props_6 = singleton_1((elems_2 = [react.createElement(Router, void 0)], ["children", Interop_reactApi.Children.toArray(Array.from(elems_2))])), createElement("div", createObj(Helpers_combineClasses("hero-body", props_6)))), (elms_2 = singleton_1((props_10 = ofArray([["className", "is-fluid"], (elems_3 = [createElement("div", createObj(Helpers_combineClasses("content", ofArray([["className", "has-text-centered"], ["children", "© 2022 General Digital Corporation"]]))))], ["children", Interop_reactApi.Children.toArray(Array.from(elems_3))])]), createElement("div", createObj(Helpers_combineClasses("container", props_10))))), createElement("div", {
        className: "hero-foot",
        children: Interop_reactApi.Children.toArray(Array.from(elms_2)),
    }))], ["children", Interop_reactApi.Children.toArray(Array.from(elems_5))])]);
    return createElement("section", createObj(Helpers_combineClasses("hero", props_13)));
}

export function App(pca) {
    const props_2 = ofArray([["instance", pca], ["children", [react.createElement(PageContainer, void 0)]]]);
    return Interop_reactApi_1.createElement(msalProvider, createObj(props_2));
}

render(App(createClient(Msal_config)), document.getElementById("elmish-app"));

This works, but now I get a different set of warnings that I have no idea how to resolve:

Each child in a list should have a unique "key" prop.

I don't know how to add a key prop, because I need to return ReactElement and props are IReactProperty.

Also, as an aside, what are the benefits of moving to Fable 4 + Feliz 2 in terms of writing a React program?

sonicbhoc avatar Jan 27 '23 18:01 sonicbhoc