Component

A component in Resquare is a piece of UI that you can reuse in other components or render to the user, it can receive props (i.e., input), store state, and return corresponding elements.

Component vs. Element?#

TODO

Declare component#

You can declare a component without props, with props, or with optional props.

Component without props#

val noPropsComponent = declareComponent {
div()
}

Component with props#

We strongly recommend you use data class as props class.

data class WithPropsComponentProps(
val name: String,
val age: Int,
)
val withPropsComponent = declareComponent { props: WithPropsComponentProps ->
div()
}

Component with optional props#

The way to create a component with optional props input is to provide a default props factory. When the component is called without props input, the default props will be used.

data class WithPropsComponentProps(
val name: String = "Default Name",
val age: Int = 20,
)
val withPropsComponent = declareComponent(::WithPropsComponentProps) { props ->
div()
}

Node type#

You can return a node as a result of the component, and there have few types of node:

  • Element (div)
  • Component
  • List of node
  • Children list of node
  • Null

Element#

val testComponent = declareComponent {
div() // returning a div element node
}

Component#

// assume we have declared the above `testComponent`
val anotherComponent = declareComponent {
testComponent() // returning a component node
}

List of node#

You can return a list of nodes as a single node. If you are nesting a list inside another list, all of them will be flattened as 1 list at the final stage.

data class ListComponentProps(
val players: List<Player>,
)
val playerHpListComponent = declareComponent { props: ListComponentProps ->
fun calculateHealthPercentage(player: Player): Double {
val maxHealth = player.getAttribute(Attribute.GENERIC_MAX_HEALTH)!!.value
return (player.health / maxHealth) * 100
}
+(props.players.map { player ->
div(props = DivProps(
style = styleOf {
height = 1.px
width = calculateHealthPercentage(player).percent
},
item = ItemStack(Material.RED_STAINED_GLASS_PANE),
))
})
}

Component key in list#

If you are returning a component as a list element, you must provide a key for Resquare to recognize the element, or you will receive a warning from Resquare.

Key can be any unique string or number identifier, such as id. If there have no way to provide a unique identifier for it, you can also use an index as an identifier, but it may cause problems when the item order is changed.

data class ItemsListComponentProps(
val items: List<CustomItemData>,
)
val itemsListComponent = declareComponent { props ->
+props.items.map { item ->
itemRowComponent(ItemRowComponentProps(
item = item
), key = item.id)
}
}
key is important!

If you are really interested in why we need a key, you can check React document.

Children List#

The children list is basically the same as the list, but the size of it should be fixed and no key is needed.

val layoutComponent = declareComponent { props ->
childrenOf(
div(DivProps(style = styles.topBar)),
div(DivProps(style = styles.contentArea)),
)
}
tl;dr, Which list I should use?
  • Use childrenOf when you are going to make a fixed nodes list.
  • Use +list when you are going to render a data list as nodes.

โŒ Don't:

+listOf(
div(),
div(),
div(),
)

๐Ÿ‘ Do:

childrenOf(
div(),
div(),
div(),
)

โŒ Don't:

childrenOf(*list.map {
div()
}.toTypedArray())

๐Ÿ‘ Do:

+list.map {
div()
}

Null#

The null node is useful when you want to hide an element in the list by condition, it is basically equal to childrenOf(), which means it will render nothing.

Remember that null itself is not a node, so you must use + to wrap the nullable node.

data class TestComponentProps(
val showMiddleItem: Boolean,
)
val testComponent = declareComponent { props: TestComponentProps ->
childrenOf(
div(),
+(if (props.showMiddleItem) div() else null),
div(),
)
}