Roact Health Bar

I’ve attempted to create a really basic health bar using Roact. Typically, I’d create my own component system for UIs; however, I thought I’d give Roact a try, and see how I got on. I’d just like some feedback to see if I’ve gone about this right.

image

To be honest, a Roact tutorial for creating basic Roblox UI components (e.g. health bar, money display, leaderboard, player list, etc.) would be greatly appreciated. I’d love to know how to utilise Roact properly. It seems like a great system to use, I just struggle a little bit to understand it fully.

Here's the source code for my little health bar:
local Replicated, Players = game:GetService 'ReplicatedStorage', game:GetService 'Players'
local Roact, Player = require(Replicated.Roact), Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()

local HealthBar = Roact.PureComponent:extend 'HealthBar'

function HealthBar:init()
	local Humanoid = Player.Character:WaitForChild 'Humanoid'
	
	self.state = {
		health = Humanoid.Health,
		maxhealth = Humanoid.MaxHealth
	}
	
	local function UpdateHealthState()
		self:setState({
			health = Humanoid.Health,
			maxhealth = Humanoid.MaxHealth
		})
	end
	
	local _HumanoidChangedSignal = Humanoid.Changed:Connect(UpdateHealthState)
	
	Player:GetPropertyChangedSignal 'Character':Connect(function()
		_HumanoidChangedSignal:Disconnect()
		
		Humanoid = Player.Character:WaitForChild 'Humanoid'
		_HumanoidChangedSignal = Humanoid.Changed:Connect(UpdateHealthState)
	end)
end

function HealthBar:render()
	return Roact.createElement('ImageLabel', {
		BackgroundTransparency = 1,
		Position = UDim2.new(0, 16, 0, 16),
		Size = UDim2.new(0, 300, 0, 18),
		ClipsDescendants = true,
		Image = 'rbxasset://textures/whiteCircle.png',
		ImageColor3 = Color3.fromRGB(225, 225, 225),
		ScaleType = Enum.ScaleType.Slice,
		SliceCenter = Rect.new(256, 256, 256, 256)
	}, {
		Amount = Roact.createElement('ImageLabel', {
			BackgroundTransparency = 1,
			Size = UDim2.new((self.state.health / self.state.maxhealth), 0, 1, 0),
			Image = 'rbxasset://textures/whiteCircle.png',
			ImageColor3 = Color3.fromRGB(48, 161, 51),
			ScaleType = Enum.ScaleType.Slice,
			SliceCenter = Rect.new(256, 256, 256, 256)
		})
	})
end

function RenderApp()
	Roact.mount(Roact.createElement(HealthBar), script.Parent, 'HealthBar')
end

RenderApp()

RoactHealthBar.rbxm (1,8 KB)

7 Likes

The code looks good to me. The one thing I’m not too sure about is beginning to listen to the character and health changing from the init method, which is meant to be used only for state initialisation.

It may be a better idea to move the event connections to the didMount lifecycle method, and disconnect the connections (and do any other cleaning up) in the willUnmount lifecyle method:

function HealthBar:init()
    self:setState({
        health = 0,
        maxHealth = 0,
    })
end

function HealthBar:didMount()
    local function updateHealth(health)
        self:setState({health = health})
    end
    local function handleCharacter(character)
        local humanoid = character:WaitForChild("Humanoid")
        self.healthChangedConnection = humanoid.HealthChanged:Connect(updateHealth)
        self.maxHealthChangedConnection = humanoid:GetPropertyChangedSignal("MaxHealth"):Connect(function()
            self:setState({maxHealth = humanoid.MaxHealth})
        end)
        self:setState({
            health = humanoid.Health,
            maxHealth = humanoid.MaxHealth,
        })
    end
    
    self.characterAddedConnection = player.CharacterAdded:Connect(handleCharacter)
    -- handle current character
    local character = player.Character
    if not character then return end
    handleCharacter(character)
end

function HealthBar:willUnmount()
    -- clean up by disconnecting connections
    self.characterAddedConnection:Disconnect()
    local healthChangedConnection = self.healthChangedConnection
    if healthChangedConnection then
        healthChangedConnection:Disconnect()
    end
    local maxHealthChangedConnection = self.MaxHealthChangedConnection
    if maxHealthChangedConnection then
        maxHealthChangedConnection:Disconnect()
    end
end

I also swapped out some Roblox events that you used for ones that seemed more appropriate.

The above code is untested and I’m inexperienced with Roact. Take anything I suggest with a grain of salt.

8 Likes