Skip to main content

memo

Check out the official React docs!

For more detailed information about React concepts and APIs, please refer to the official documentation for memo.


React.memo lets you optimize components by memoizing their output. This prevents unnecessary rerenders when the props haven't changed, improving performance for pure components.

Without memoization, a component will rerender whenever its parent rerenders, even if its props remain the same. By wrapping a component with React.memo, React will skip rendering the component and reuse the last rendered output if the props are unchanged.

ReactMemoComponentAttribute

The [<ReactMemoComponent>] attribute provides a convenient way to memoize functional components in Feliz. It does the transformation on transpile level so you don't have to wrap your component manually.

[<ReactMemoComponent>] // memoizes component to prevent rerender whenever parent rerenders
let ChildComponent (onClick: unit -> unit) =
// ... component implementation

Check out the ReactMemoComponentAttribute documentation for more details.

Check the output in the browser console

The child component below is memoized using the [<ReactMemoComponent>] attribute. It only rerenders when its props change.

Hello, world!
Show code
module Example.MemoAttribute

open Feliz
open Browser.Dom

[<ReactMemoComponent>]
let RenderTextWithEffect (text: string) =
React.useEffect (fun () -> console.log("Rerender!", text) )
Html.div [
prop.text text;
prop.testId "memo-attribute"
]


[<ReactComponent(true)>]
let Main () =
let isDark, setIsDark = React.useState(false)
let text, setText = React.useState("Hello, world!")
let fgColor = if isDark then color.white else color.black
let bgColor = if isDark then color.black else color.white
Html.div [
prop.style [style.border(1, borderStyle.solid, fgColor); style.padding 20; style.color fgColor; style.backgroundColor bgColor]
prop.children [
Html.h3 "Check the output in the browser console"
Html.p "The child component below is memoized using the [<ReactMemoComponent>] attribute. It only rerenders when its props change."
Html.button [
prop.text "Toggle Dark Mode"
prop.onClick (fun _ -> setIsDark(not isDark))
]
Html.input [
prop.value text
prop.onChange setText
]
RenderTextWithEffect(text)
]
]

React.memo function

warning

This approach has several limitations compared to using the [<ReactMemoComponent>] attribute (see example below):

  • You must use a let binding for your component, to ensure Fable transpiling as const.
  • You must use any F# type transpiling to a JavaScript object for props (e.g., anonymous record, [<PojoAttribute>]).
let MemoizedComponent =
React.memo<{|text: string|}> (fun props ->
// ... component implementation
)

Check the output in the browser console

Hello, world!
Show code
module Example.MemoFunction

open Feliz
open Browser.Dom

[<ReactComponent>]
let RenderTextWithEffect (text: string) =
React.useEffect (fun () -> console.log("Rerender!", text) )
Html.div [
prop.text text;
prop.testId "memo-attribute"
]

let MemoFunction =
React.memo<{|text: string|}> (fun props ->
RenderTextWithEffect(props.text)
)

[<ReactComponent(true)>]
let Main () =
let isDark, setIsDark = React.useState(false)
let text, setText = React.useState("Hello, world!")
let fgColor = if isDark then color.white else color.black
let bgColor = if isDark then color.black else color.white
Html.div [
prop.style [style.border(1, borderStyle.solid, fgColor); style.padding 20; style.color fgColor; style.backgroundColor bgColor]
prop.children [
Html.h3 "Check the output in the browser console"
Html.button [
prop.text "Toggle Dark Mode"
prop.onClick (fun _ -> setIsDark(not isDark))
]
Html.input [
prop.value text
prop.onChange setText
]
React.memoRender(MemoFunction, {| text = text |})
]
]

areEqual

The React.memo function also accepts an optional second argument, areEqual, which is a custom comparison function for props. This function receives the previous and next props and should return true if they are equal (i.e., no rerender needed) or false if they are different (i.e., rerender needed).

This is useful as memo only does a shallow comparison of props by default. If your props are complex objects or arrays, you may need to provide a custom comparison function to accurately determine equality.

Check the output in the browser console

Apple, Banana, Orange
Show code
module Example.MemoFunctionAreEqual

open Feliz
open Browser.Dom

[<ReactComponent>]
let RenderArrayWithEffect (fruitArray: string []) =
React.useEffect (fun () -> console.log("Rerender!", fruitArray) )
Html.div [
prop.text (String.concat ", " fruitArray);
prop.testId "memo-attribute"
]

let MemoFunction =
React.memo<{|fruitArray: string[]|}> (
(fun props ->
RenderArrayWithEffect(props.fruitArray)),
(fun prevProps nextProps ->
prevProps.fruitArray.Length = nextProps.fruitArray.Length &&
prevProps.fruitArray |> Array.forall2 (=) nextProps.fruitArray
)
)

[<ReactComponent(true)>]
let Main () =
let isDark, setIsDark = React.useState(false)
let fruits = [| "Apple"; "Banana"; "Orange" |]
let fgColor = if isDark then color.white else color.black
let bgColor = if isDark then color.black else color.white
Html.div [
prop.style [style.border(1, borderStyle.solid, fgColor); style.padding 20; style.color fgColor; style.backgroundColor bgColor]
prop.children [
Html.h3 "Check the output in the browser console"
Html.button [
prop.text "Toggle Dark Mode"
prop.onClick (fun _ -> setIsDark(not isDark))
]
React.memoRender(MemoFunction, {| fruitArray = fruits |})
]
]