Hi, I'm hoping you lovely people can help me out w...
# redux
t
Hi, I'm hoping you lovely people can help me out with my approach to updating state. I've been using data classes to represent State and the nested values on it, but I'm finding this cumbersome for some use cases, especially with nested data:
state.copy(levelOne = levelOne.copy(levelTwo = levelTwo.copy( value = 0 )))
which may not seem too bad here, but does get quiet verbose when I'm trying to update several values at two different 'paths'. Additionally I need to get/set values dynamically (think lodash's set/get functionality) i.e.,
get<T>(state, "levelOne.levelTwo.value)
and
set(state, "levelOne.levelTwo.value", 1)
. I should note my state is being serialised/deserialised to/from Firestore as a Map. To accomplish the above, I'm converting my hierarchy of data classes to a map (with nested maps) using jackson, and then implementing the set/get functionality on those maps, before converting back to the original State data class hierachy. But this seems needlessly complex. For example, I have to convert everything in the specified update path to a mutable structure, transverse the maps, and do lots of casting along the way. But, the benefit is that nested access is straightforward and my reducers are simple:
Copy code
reducer(state: State, action: FSA) {
   val payload = action.payload
   // I actually do this in my root reducer so i just mutate state directly here
   val fresh = state.copy()
   val oldValue = get<Int>("levelOne.levelTwo.value")
   set(fresh, "levelOne.levelTwo.value", 3 * oldValue)
   return fresh
}
I'm thinking one improvement I could do is implement something like immerjs where state is 'patched' in reducers and the patches are applied all at once before the root reducer finally returns the next state to minimise my converting to/from maps. The friction here stopping me from going to Maps altogether, is with data classes I get type safety when reading most values from state normally ...
val some = tate.levelOne.levelTwo.value
. Grateful for your thoughts/objections to the approaches above 🙏
Has anyone tried this library https://immutables.github.io/ ?
p
Would be helpful to state the platform(s) you are targeting. There are a mix of js/Android/iOS/etc devs in here. Sounded like you were targetting JS until I saw the link to Immutables. Assuming your on jvm only. Keeping the state as flat as possible will help. Also breaking apart reducers for substates helps, which looks like your already doing. Whether data types or maps is best is probably up to your use case and code base. I've worked on an app that held everything as a JsonObject and required a lot of casting. Worked, but was cumbersome and lost a lot of type safety.
What would be benefit of immutable.github.io over data classes & kotlin immutable objects?
t
I haven't used the immutable library but it popped up in my searching so was curious if anyone else had tried it. I'm targetting the JVM currently, but do plan on targetting other environments in the future (hopefully).
I'm currently leaning towards a mixture of a builder pattern + Mutable and Immutable version of my state where the immutable version is simply a data class with some helper methods. Specifically it has a patch method ... resulting code looks like this (to be tested):
Copy code
val next = state.patch {
  id = "new-id"
  state.attributes.add(2)
  set("some.nested.path", listOf(1, 2, 3))
}
The Immutable version is again a data class but the variables are backed by a map under the hood which allows for dynamically accessing/setting values
p
ah. keep in mind that lib would not work on other platforms...
👍 1
t
great pt hadn't occurred to me but would in 5 months 😂
p
interesting. yes, nested state can be cumbersome. I've played around with different helper function and patterns that have helped, but I don't have any handy. I hope to look into the kotlin compiler api once it is available, there may be some interesting things that could help
t
I haven't seen a slick approach to yet it either (obs) but I can't image everyone has a flat state so I'm sure better solutions will emerge eventually!
My patch method above seems promising to me currently though ... it has more boilerplate than I'd like, but im happy with it so far and if it works well i'll put some time into refactoring
s
t
not quiet what I need from what I can tell ... im giving Arrow's optics a try now 🤞