Skip to content
This repository has been archived by the owner on Jul 30, 2022. It is now read-only.

Commit

Permalink
Merge pull request #1 from ClockworkSquirrel/update-tables
Browse files Browse the repository at this point in the history
Table Fixes+Deep Merging
  • Loading branch information
cxmeel authored Aug 27, 2020
2 parents 78c7f11 + cd18bf7 commit 18d1281
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 28 deletions.
38 changes: 23 additions & 15 deletions docs/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Creates a new state object. Accepts an optional `InitialState` parameter, for de

## `State:Set()`
-----
Sets the value of a given key in the state, and then fires off any `Changed` signals. You should always use this when you need to change the state. Never modify state directly, unless using `RawSet`!
Sets the value of a given key in the state, and then fires off any `Changed` signals. You should always use this when you need to change the state. Use `:RawSet()` to change values without invoking change events.

### Syntax
`State:Set(Key: any, Value: any): void`
Expand All @@ -16,9 +16,6 @@ Sets the value of a given key in the state, and then fires off any `Changed` sig
-----
Set multiple values in the state. `Changed` signals will be fired for each modified key.

!!! warning
Setting sub-tables will fully overwrite their contents in the state. This method uses shallow-merging, which only merges the values at the root of the state. Use :Get() and append/overwrite keys where required, and set the modified table when storing tables.

### Syntax
`State:SetState(StateTable: Dictionary<any, any>): void`

Expand All @@ -29,21 +26,29 @@ local State = BasicState.new({
Greetings = {
Place = "Welcome to the Mountain!",
Roblox = "Hey Roblox!",
Me = "Hi ClockworkSquirrel!"
Me = "Hi csqrl!"
}
})

local function ChangeLocations(NewLocation)
local NewGreetings = State:Get("Greetings")
NewGreetings.Place = string.format("Hello %s!", NewLocation)
State:SetState({
Location = "City",
Greetings = {
Place = "Welcome to the City!"
}
})

State:SetState({
Location = NewLocation,
Greetings = NewGreetings
})
end
--[[
The new state object will look like this:
ChangeLocations("City")
{
Location = "City",
Greetings = {
Place = "Welcome to the City!",
Roblox = "Hey Roblox!",
Me = "Hi csqrl!"
}
}
--]]
```

## `State:Toggle()`
Expand Down Expand Up @@ -86,7 +91,7 @@ local State = BasicState.new({
local function BuyItem(ItemName, ItemPrice)
-- A cap of 0 was specified to prevent Money from going below 0
State:Decrement("Money", ItemPrice, 0)
print(("Bought %s for %d"):format(ItemName, ItemPrice))
print(string.format("Bought %s for %d", ItemName, ItemPrice))
end

BuyItem("Noodles", 12)
Expand Down Expand Up @@ -164,6 +169,9 @@ There's a full example within the `/examples` directory on how to use BasicState
-----
An [RBXScriptSignal](https://developer.roblox.com/en-us/api-reference/datatype/RBXScriptSignal) which is fired any time the state mutates. The Event fires with the following values (in order):

!!! warning
Using `:GetChangedSignal()` is the preferred method for listening to state changes.

| Name | Type | Description |
|-----------------|--------------------------------|-------------------------------------------------------|
| `OldState` | `Dictionary<any, any>` | The entire state object prior to mutation. |
Expand Down
53 changes: 40 additions & 13 deletions src/init.lua
Original file line number Diff line number Diff line change
@@ -1,23 +1,51 @@
--[[
BasicState by ClockworkSquirrel
Version: 0.1.0
BasicState by csqrl (ClockworkSquirrel)
Version: 0.1.1
Documentation is at:
https://clockworksquirrel.github.io/BasicState/
Overview of Methods:
BasicState.new([ InitialState: Dictionary<any, any> = {} ]): State
State:Set(Key: any, Value: any): void
State:SetState(StateTable: Dictionary<any, any>): void
State:Toggle(Key: any): void
State:Increment(Key: any[, Amount: Number = 1][, Cap: Number = nil]): void
State:Decrement(Key: any[, Amount: Number = 1][, Cap: Number = nil]): void
State:RawSet(Key: any, Value: any): void
State:Get(Key: any[, DefaultValue: any = nil]): any
State:GetState(): Dictionary<any, any>
State:GetChangedSignal(Key: any): RBXScriptSignal
State:Destroy(): void
State:Roact(Component: Roact.Component[, Keys: any[] = nil]): Roact.Component
State.Changed: RBXScriptSignal
--]]

local State = {}

--[[
Helper function which creates a shallow copy of passed tables.
Child tables will not be copied, and passed ByRef, meaning
modifying them will affect the original copy
Helper function which creates a deep copy of passed tables.
In v0.1.1, JoinDictionary now performs a deep copy of tables. This
allows nested tables within state to be modified without losing
original data.
--]]
local function JoinDictionary(...)
local NewDictionary = {}

for _, Dictionary in next, { ... } do
if (type(Dictionary) ~= "table") then
continue
end

for Key, Value in next, Dictionary do
if (type(Value) == "table") then
NewDictionary[Key] = JoinDictionary(NewDictionary[Key], Value)
continue
end

NewDictionary[Key] = Value
end
end
Expand Down Expand Up @@ -66,7 +94,7 @@ function State.new(InitialState)
end

--[[
Return a shallow copy of the current stored state
Return a deep copy of the current stored state
--]]
function State:GetState()
return JoinDictionary(self.__state, {})
Expand All @@ -86,6 +114,10 @@ end
function State:Set(Key, Value)
local OldState = self:GetState()

if (type(Value) == "table") then
Value = JoinDictionary(OldState[Key], Value)
end

if (OldState[Key] ~= Value) then
self:RawSet(Key, Value)
self.__changeEvent:Fire(OldState, Key)
Expand All @@ -94,13 +126,8 @@ end

--[[
Like React's setState method, SetState accepts a table of key-value pairs,
which will be added to or mutated in the store. This is a shallow-merge,
and therefore sub-tables will be fully overwritten by whatever value
is specified using this method.
Be sure to Get() a copy of the currently stored table, overwrite or append
relevant keys, and pass the modified table into this method, when setting
table values.
which will be added to or mutated in the store. This is a deep copy, so
original data will not be overwritten unless specified.
--]]
function State:SetState(StateTable)
assert(type(StateTable) == "table")
Expand Down

0 comments on commit 18d1281

Please sign in to comment.