Code Splitting
There are two components involved when implementing code splitting: React.lazy'
and React.suspense
.
React.lazy'
This function allows you to dynamically import a component from another file asynchronously.
The benefit of this is it allows your bundler (such as webpack) to keep those files separate. The benefit of this is that you get faster initial renders in your application.
React.suspense
This component allows you to provide children that if they are loading an element will be rendered as a placeholder while they load.
This works for anything in the child tree, so you could for example put suspense
at the top of your
application if you were so inclined.
In summary, place suspense
where you want to see the loading indicator and lazy'
where you want
to do the code splitting.
You must define the dynamic import at the module level, or it will fail!
Show code
- CodeSplitting.fs
- Counter.fs
module Example.CodeSplitting
open Fable.Core
open Feliz
let MyNonCodeSplitComponent() =
Html.div [
prop.text "I was loaded synchronously!"
]
let asyncComponent : JS.Promise<unit -> ReactElement> = JsInterop.importDynamic "./Counter.fs"
[<ReactComponent(true)>]
let CodeSplitting(delay: int option) =
Html.div [
prop.children [
MyNonCodeSplitComponent()
if delay.IsSome then
Html.div [
prop.text $"I will be loaded after {delay.Value} milliseconds!"
]
React.suspense(
[
Html.div [
React.lazy'((fun () ->
promise {
if delay.IsSome then
do! Promise.sleep (delay.Value)
return! asyncComponent
}
),())
]
],
Html.div [
prop.text "Loading..."
]
)
]
]
module Example.React
open Fable.Core
open Feliz
[<Erase; Mangle(false)>]
type Counter =
[<ReactComponent(true)>]
static member Counter() =
let (count, setCount) = React.useState 0
Html.div [
Html.h1 count
Html.button [
prop.text "Increment"
prop.onClick (fun _ -> setCount(count + 1))
]
]
This doesn't appear to be doing anything, this is because the file loads so quickly. To get
a better unstanding of what's happening we can map the JS.Promise
to add a delay:
Show code
- CodeSplitting.fs
- Counter.fs
module Example.CodeSplitting
open Fable.Core
open Feliz
let MyNonCodeSplitComponent() =
Html.div [
prop.text "I was loaded synchronously!"
]
let asyncComponent : JS.Promise<unit -> ReactElement> = JsInterop.importDynamic "./Counter.fs"
[<ReactComponent(true)>]
let CodeSplitting(delay: int option) =
Html.div [
prop.children [
MyNonCodeSplitComponent()
if delay.IsSome then
Html.div [
prop.text $"I will be loaded after {delay.Value} milliseconds!"
]
React.suspense(
[
Html.div [
React.lazy'((fun () ->
promise {
if delay.IsSome then
do! Promise.sleep (delay.Value)
return! asyncComponent
}
),())
]
],
Html.div [
prop.text "Loading..."
]
)
]
]
module Example.React
open Fable.Core
open Feliz
[<Erase; Mangle(false)>]
type Counter =
[<ReactComponent(true)>]
static member Counter() =
let (count, setCount) = React.useState 0
Html.div [
Html.h1 count
Html.button [
prop.text "Increment"
prop.onClick (fun _ -> setCount(count + 1))
]
]
See the React docs for more information about code splitting.