Skip to content

Commit 5dad0c6

Browse files
vbfoxalfonsogarciacaro
authored andcommitted
Simplify memo usage by default
This is a breaking change to memo and memoWith ! They become builder functions, producing functions that can be directly used in a render method and don't support children. The high-level version are still available in the ReactElementType module for anyone wanting the more complete experience. Fixes #118
1 parent ab756fc commit 5dad0c6

File tree

1 file changed

+69
-30
lines changed

1 file changed

+69
-30
lines changed

src/Fable.React/Fable.Helpers.React.fs

Lines changed: 69 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,8 @@ let inline ofArray (els: ReactElement array): ReactElement = unbox els
888888
[<Emit("null")>]
889889
let nothing: ReactElement = jsNative
890890

891+
type PropsEqualityComparison<'props> = 'props -> 'props -> bool
892+
891893
[<RequireQualifiedAccess>]
892894
module ReactElementType =
893895
let inline ofComponent<'comp, 'props, 'state when 'comp :> Component<'props, 'state>> : ReactComponentType<'props> =
@@ -903,45 +905,82 @@ module ReactElementType =
903905
let inline create<'props> (comp: ReactElementType<'props>) (props: 'props) (children: ReactElement seq): ReactElement =
904906
createElement(comp, props, children)
905907

906-
type PropsEqualityComparison<'props> = 'props -> 'props -> bool
907-
908-
[<Import("memo", from="react")>]
909-
let private reactMemo<'props> (render: 'props -> ReactElement) : ReactComponentType<'props> =
910-
jsNative
911-
912-
/// React.memo is a higher order component. It’s similar to React.PureComponent but for function components instead of
913-
/// classes.
908+
/// React.memo is a higher order component. It’s similar to React.PureComponent but for function components instead of
909+
/// classes.
910+
///
911+
/// If your function component renders the same result given the same props, you can wrap it in a call to React.memo
912+
/// for a performance boost in some cases by memoizing the result. This means that React will skip rendering the
913+
/// component, and reuse the last rendered result.
914+
///
915+
/// By default it will only shallowly compare complex objects in the props object. If you want control over the
916+
/// comparison, you can use `memoWith`.
917+
[<Import("memo", from="react")>]
918+
let inline memo<'props> (render: 'props -> ReactElement) : ReactComponentType<'props> =
919+
jsNative
920+
921+
[<Import("memo", from="react")>]
922+
let private reactMemoWith<'props> (render: 'props -> ReactElement, areEqual: PropsEqualityComparison<'props>) : ReactComponentType<'props> =
923+
jsNative
924+
925+
/// React.memo is a higher order component. It’s similar to React.PureComponent but for function components instead of
926+
/// classes.
927+
///
928+
/// If your function component renders the same result given the same props, you can wrap it in a call to React.memo
929+
/// for a performance boost in some cases by memoizing the result. This means that React will skip rendering the
930+
/// component, and reuse the last rendered result.
931+
///
932+
/// This version allow you to control the comparison used instead of the default shallow one by provide a custom
933+
/// comparison function.
934+
let memoWith<'props> (areEqual: PropsEqualityComparison<'props>) (render: 'props -> ReactElement) : ReactComponentType<'props> =
935+
reactMemoWith(render, areEqual)
936+
937+
/// memo is similar to React.PureComponent but is built from only a render function.
914938
///
915-
/// If your function component renders the same result given the same props, you can wrap it in a call to React.memo
916-
/// for a performance boost in some cases by memoizing the result. This means that React will skip rendering the
917-
/// component, and reuse the last rendered result.
939+
///
940+
/// If your function renders the same result given the same props, you can wrap it in a call to memo for a performance
941+
/// boost in some cases by memoizing the result. This means that React will skip rendering the component, and reuse the
942+
/// last rendered result.
943+
///
944+
/// The resulting function shouldn't be used directly in a render but should be stored to be reused :
945+
///
946+
/// ```
947+
/// type HelloProps = { Name: string }
948+
/// let hello = memo "Hello" (fun { Name = name } -> span [str "Hello "; str name])
949+
///
950+
/// let view model =
951+
/// hello { Name = model.Name }
952+
/// ```
918953
///
919954
/// By default it will only shallowly compare complex objects in the props object. If you want control over the
920955
/// comparison, you can use `memoWith`.
921-
let memo<'props> (name: string) (render: 'props -> ReactElement) : ReactComponentType<'props> =
956+
let memo<'props> (name: string) (render: 'props -> ReactElement) : 'props -> ReactElement =
922957
render?displayName <- name
923-
reactMemo(render)
958+
let memoType = ReactElementType.memo render
959+
fun props ->
960+
ReactElementType.create memoType props []
924961

925-
[<Import("memo", from="react")>]
926-
let private reactMemoWith<'props> (render: 'props -> ReactElement, areEqual: PropsEqualityComparison<'props>) : ReactComponentType<'props> =
927-
jsNative
928-
929-
/// React.memo is a higher order component. It’s similar to React.PureComponent but for function components instead of
930-
/// classes.
962+
/// memoWith is similar to React.Component but is built from a render function and an equality
963+
/// (Inverse of shouldComponentUpdate) function.
964+
///
965+
/// If your function renders the same result given the "same" props (According to areEqual), you can wrap it in a call
966+
/// to memoWith for a performance boost in some cases by memoizing the result. This means that React will skip rendering
967+
/// the component, and reuse the last rendered result.
931968
///
932-
/// If your function component renders the same result given the same props, you can wrap it in a call to React.memo
933-
/// for a performance boost in some cases by memoizing the result. This means that React will skip rendering the
934-
/// component, and reuse the last rendered result.
969+
/// The resulting function shouldn't be used directly in a render but should be stored to be reused :
935970
///
936-
/// This version allow you to control the comparison used instead of the default shallow one by provide a custom
937-
/// comparison function.
938-
let memoWith<'props> (name: string) (areEqual: PropsEqualityComparison<'props>) (render: 'props -> ReactElement) : ReactComponentType<'props> =
971+
/// ```
972+
/// type HelloProps = { Name: string }
973+
/// let helloEquals p1 p2 = p1.Name = p2.Name
974+
/// let hello = memoWith "Hello" helloEquals (fun { Name = name } -> span [str "Hello "; str name])
975+
///
976+
/// let view model =
977+
/// hello { Name = model.Name }
978+
/// ```
979+
let memoWith<'props> (name: string) (areEqual: PropsEqualityComparison<'props>) (render: 'props -> ReactElement) : 'props -> ReactElement =
939980
render?displayName <- name
940-
reactMemoWith(render, areEqual)
941-
942-
/// Create a ReactElement to be rendered from an element type, props and children
943-
let inline ofElementType<'props> (comp: ReactElementType<'props>) (props: 'props) (children: ReactElement seq): ReactElement =
944-
ReactElementType.create comp props children
981+
let memoType = ReactElementType.memoWith areEqual render
982+
fun props ->
983+
ReactElementType.create memoType props []
945984

946985
#else
947986
/// Alias of `ofString`

0 commit comments

Comments
 (0)