fable-compiler / fable-react

Fable bindings and helpers for React and React Native
MIT License
275 stars 66 forks source link

Document process of using a third party react component #84

Open danyx23 opened 6 years ago

danyx23 commented 6 years ago

I think one of the big advantages of using Fable and React over e.g. Elm is that it is possible to use an ocean full of ready made React components. However, it is somewhat unclear what needs to be done to declare the required props etc. to use a third party React component. It would be great if this repo in the readme or the Fable docs could have a section on how to do this.

I don't know what the potential issues are with various kinds of e.g. Higher Order Components etc but it might be worth spelling these out explicitly as well. Please let me know if there is anything I can help with in particular to bring this forward.

alfonsogarciacaro commented 6 years ago

Hi @danyx23! You're right, we have the pieces to easily import React components but we're missing the guide to do it. There are currently two ways to do it:

EDIT: @danyx23 documented how to use 3rd party React components. You can also see below an example of importing HOC components.

danyx23 commented 6 years ago

Hi @alfonsogarciacaro, thank your for your reply! How do you suggest to go forward with this? Should I create a PR with an initial draft of a documentation document in the docs folder of this repo that outlines how to use a third party react component?

I am relatively new to Fable and have only used React a little bit - are there areas that are more tricky, like HOCs or similar somewhat advanced features of React?

alfonsogarciacaro commented 6 years ago

This is funny, I was about to write that I hadn't used React HOCs so I didn't know, but just today I had to use them in my project and realized we're missing a helper in fable-react. I think we could add something like this:

open Fable.Core
open Fable.Import.React
open Fable.Helpers.React
open Fable.Helpers.React.Props

// Helper to be added to fable-react
let importHigherOrderComponent<[<Pojo>]'P> (importMember: string) (importPath: string) (fn: 'P->ReactElement): ComponentClass<'P> = jsNative

type [<Pojo>] MyProps =
    { index: int; value: string }

let SortableItem: ComponentClass<MyProps> =
    importHigherOrderComponent "SortableElement" "react-sortable-hoc" (fun p ->
        li [] [str p.value])

// To be used like...
let render () =
    from SortableItem { index = 1; value = "foo" } []

But I'm not sure about a few things:

@zaaack @MangelMaxime what do you think?

@danyx23 About the documentation, if you could send a PR that'd be great. Don't worry too much about the details, we can work on that later together. The important thing to keep in mind is Fable compiles directly to JS, not JSX. So usually calling React.createElement, passing the component as the first element, a plain JS object containing the props, and filling the rest of arguments with children (as the fable-react helpers do) usually works.

MangelMaxime commented 6 years ago

I never used HOC so I don't know and don't really understand the hight order think.

alfonsogarciacaro commented 6 years ago

@MangelMaxime They're used for example for react-sortable-hoc.

forki commented 6 years ago

Please please don't call it higher order. Let's try to think hard for a serious name

Alfonso Garcia-Caro notifications@github.com schrieb am Di., 17. Apr. 2018, 23:11:

@MangelMaxime https://github.com/MangelMaxime They're used for example for react-sortable-hoc https://github.com/clauderic/react-sortable-hoc#usage.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/fable-compiler/fable-react/issues/84#issuecomment-382147663, or mute the thread https://github.com/notifications/unsubscribe-auth/AADgNNPbIEGEVTfPgffrY4xQoNR3xKdgks5tplpogaJpZM4TVog4 .

danyx23 commented 6 years ago

@alfonsogarciacaro cool I'll draft a PR in the next few days. I can't really comment on the finer points yet but I'll go over the docs again and look at some usage, maybe I will have a bit more input then.

@forki it's a silly name but it what is used in the official react docs: https://reactjs.org/docs/higher-order-components.html. As such I would stay with higher order components as that is what people coming from react would be looking for and how third party components that follow this pattern would describe themselves.

zaaack commented 6 years ago

I know react-sortable-hoc, but never use it before. I'm OK with the naming, but not sure about the details, it's a rare case for me, but it looks really useful.

alfonsogarciacaro commented 6 years ago

Thanks a lot for your comments! Yes, so far we've tried to closely follow the React terminology to prevent mismatches with React documentation. We should keep the reference to HOC in this case here too.

alfonsogarciacaro commented 6 years ago

I forgot to mention @vbfox to know his opinion about the helper for React HOCs (please see comment above).

danyx23 commented 6 years ago

I added a first draft of the docs - please let me know what you think!

runefs commented 6 years ago

Did you make any progress on the importHigherOrderComponent? Was look for a way to integrate react-sortable-hoc into my project to day and found this thread but couldn't figure out whether you have implemented it y a different name

MangelMaxime commented 6 years ago

Look at this comments: https://github.com/fable-compiler/fable-react/issues/84#issuecomment-382143748

I think it should give you a solution

alfonsogarciacaro commented 6 years ago

@runefs We haven't added the helper yet to Fable.React but here's how I'm using react-sortable-hoc in my project, hope it helps:

Using react-sortable-hoc from Fable ```fsharp open Fable.Helpers.React open Fable.Core.JsInterop /// Ugly helper that should be moved to fable-react let inline hocOfImport<'P> (importMember: string) (importPath: string) (fn: 'P->ReactElement): ComponentClass<'P> = !!((import importMember importPath) $ fn) // let arrayMove(xs: 'a[], oldIndex: int, newIndex: int): 'a[] = importMember "react-sortable-hoc" type SortableElementProps<'a> = { // SortableElement HOC props (cannot be used by wrapped component) // https://github.com/clauderic/react-sortable-hoc#sortableelement-hoc index: int disabled: bool // Custom props rowIndex: int key: string value: 'a handle: ComponentClass renderItem: ComponentClass -> int -> 'a -> ReactElement } type SortEnd = { oldIndex: int newIndex: int } type SortableContainerProps<'a> = { // SortableContainer HOC props (cannot be used by wrapped component) // https://github.com/clauderic/react-sortable-hoc#sortablecontainer-hoc onSortEnd: SortEnd->unit lockAxis: string // Custom props items: 'a list handle: ComponentClass useDragHandle: bool renderItem: ComponentClass -> int -> 'a -> ReactElement renderList: ReactElement list -> ReactElement disabled: bool } let mkSortableHandle (f: unit -> ReactElement) = hocOfImport "SortableHandle" "react-sortable-hoc" f let mkSortableElement<'a> () = hocOfImport "SortableElement" "react-sortable-hoc" (fun (p: SortableElementProps<'a>) -> p.renderItem p.handle p.rowIndex p.value) let mkSortableContainer<'a> (sortableElement: ComponentClass>) = hocOfImport "SortableContainer" "react-sortable-hoc" (fun (p: SortableContainerProps<'a>) -> p.items |> List.mapi (fun i v -> let a = { index = i disabled = p.disabled rowIndex = i key = "item-" + string i // TODO: unique keys not depending on order value = v handle = p.handle renderItem = p.renderItem } from sortableElement a []) |> p.renderList) type SortableComponentProps<'a> = { renderHandle: unit->ReactElement renderItem: ComponentClass -> int -> 'a -> ReactElement renderList: ReactElement list -> ReactElement items: 'a list /// oldIndex: int -> newIndex: int -> unit handleSortChanged : int -> int -> unit disabled: bool } type SortableComponent<'a>(p) = inherit React.PureComponent, unit>(p) // Cache the SortableContainer HOC in the constructor so it's only necessary to build it once let sortableContainer = mkSortableElement () |> mkSortableContainer let sortableHandle = mkSortableHandle p.renderHandle override this.render() = from sortableContainer { // arrayMove(List.toArray this.props.items, i.oldIndex, i.newIndex) onSortEnd = fun i -> this.props.handleSortChanged i.oldIndex i.newIndex lockAxis = "y" items = this.props.items handle = sortableHandle useDragHandle = true renderItem = this.props.renderItem renderList = this.props.renderList disabled = this.props.disabled } [] /// handleSortChanged: oldIndex: int -> newIndex: int -> unit let mkSortable (enabled: bool) (renderList: ReactElement list -> ReactElement) (renderItem: ComponentClass -> int -> 'a -> ReactElement) (renderHandle: unit -> ReactElement) (items: 'a list) (handleSortChanged: int -> int -> unit) = ofType,_,_> { renderHandle = renderHandle renderItem = renderItem renderList = renderList items = items handleSortChanged = handleSortChanged disabled = not enabled } [] ```
olivercoad commented 4 years ago

Using a discriminated union props type as documented in using-third-party-react-components.md, how do you add support for IHTMLProp? Fulma has a Props of IHTMLProp list case in the options DU types but I'm not sure how to do that for 3rd party react components.

alfonsogarciacaro commented 4 years ago

Unfortunately this involves a bit of a trick at the moment. When passing the props to react we need to convert them to a JS object, which is done with keyValueList. Right now, props nested in another union case are not supported by default, so you have to make the conversion by cheating the compiler as it's done for example here with the Style props: https://github.com/fable-compiler/fable-react/blob/69ec32d30e6453954bb3d1ad4fcd32627f842abb/src/Fable.React.Props.fs#L379-L387