In this post, we'll look at how React components differ from React elements, and how you can make React components using Feliz. Let's dive in!
React elements
A React element is a plain JavaScript object that describes what you want to see on the screen. When working directly in JavaScript, it's common to use JSX to create a React element:
const element = (
<div>
<h1 className="heading">Hello world!</h1>
<p>This is a React element.</p>
</div>
);
The resulting JavaScript object is similar to this:
const element = {
type: 'div',
props: {
children: [
{
type: 'h1',
props: {
className: 'heading',
children: 'Hello world!'
}
},
{
type: 'p',
props: {
children: 'This is a React element.'
}
}
]
}
};
The React element has all the information that React needs to render the element into the DOM. When you want to change the DOM, you give React a new React element (elements are immutable) to render. One of React's selling points is the ability to rerender efficiently.
React components
Components let you split the UI into independent, reusable pieces, and think about each piece in isolation.
A React component is given properties and returns a React element describing how the component should be rendered:
function Bio(props) {
return (
<div>
<h2>{props.name}</h2>
<img src={props.user.bioImgUrl} alt={props.name}/>
<div>Emoji summary: {props.emoji}</div>
</div>
);
}
function App() {
return (
<div>
<Bio name="Kash" bioImgUrl="..." emoji="🐱👤" />
<Bio name="Matt" bioImgUrl="..." emoji="🧐" />
</div>
);
}
Creating the component in Feliz is easy:
[<ReactComponent>]
let Bio(name, bioImgUrl, emoji) =
Html.div [
Html.img [ prop.src bioImgUrl; prop.alt name ]
Html.h1 Html.text name
Html.div $"Emoji summary: %s{emoji}"
]
But you could get similar behaviour from a regular F# function that returns a React element:
let bio name bioImgUrl emoji =
Html.div [
Html.img [ prop.src bioImgUrl; prop.alt name ]
Html.h1 name
Html.div $"Emoji summary: %s{emoji}"
]
As Zaid explains, there are some benefits to creating a React component in cases such as this (notably reusing the results of the previous render if the props are the same). However, the main benefit comes from using React-specific functionality like hooks.
React hooks
For example, the useState
hook allows a component to maintain its own state, rather than relying on the values passed in as props.
[<ReactComponent>]
let Counter () =
let count, setCount = React.useState(0)
Html.div [
Html.button [
prop.text "Click me!"
prop.onClick (fun _ -> setCount(count + 1))
]
Html.div $"You've clicked the button %i{count} times!"
]
Check out the React docs on useState
and Feliz docs on useState
for more info. Ryan also has a related blog post that goes deeper, but uses an older Feliz syntax.
Other common hooks are useEffect
and useContext
; Kash has a blog post on the latter. You can see other React hooks in the hooks API reference, a lot of which are covered in the Feliz docs.
Summary
React elements describe what you want to see on the screen. React components allow you to split the UI into reusable pieces. It's occasionally useful to create components without hooks in your Feliz project, but hooks provide the main benefit of components over regular element-returning F# functions. Feliz makes this easy 🚀 Thanks Zaid and all Feliz contributors!