CoRed is Redux-like implementation that maintains the benefits of Redux's core idea without the boilerplate. No more action types, action creators, switch statements or complicated setup. It is Kotlin and it has coroutine supported right out-of-the-box. Also, yes, it is Kotlin Multiplatform supported (https://kotlinlang.org/docs/mpp-intro.html)
CoRed is an opinionated way to setup the Redux implementation. Why we need CoRed? Redux is an amazing state management tool. However, some might find it to be too complicated or hard to use/understand, because the plain vanilla Redux-like implementation is quite boilerplate-y. There is an even page from the official Redux.js on how to reduce the boilerplate. That's why we develop CoRed.
In CoRed's implementation, we are trying to solve the same problem with original Redux by introducing the more friendly API, then translating that into Kotlin in order to be a bit more accessible to mobile dev to use in their projects.
Add mavenCentral into your dependencies' repositories configuration
repositories {
mavenCentral()
}
dependencies {
// if you are working on JVM or Android only project
implementation("com.github.kittinunf.cored:cored-jvm:«version»") //for JVM support
// if you are working in KMM project
implementation("com.github.kittinunf.cored:cored:«version»") //for Kotlin Multiplatform support
}
Good question. Let's try to set up a minimal example with HashEngine with an ability to show a list data from the network.
Assuming that we have a Repository class that already connects to the API somewhere, eg. Comments, we can use it to fetch the data for our store.
interface CommentRepository {
suspend fun getComments(): List<String>
}
With the concrete class implements above interface with your preferred network engine
class CommentRepositoryImpl : CommentRepository
Redux with CoRed implementation (the setup part should be under 35~ lines)
// State definition for you application
class CommentsState(val isLoading: Boolean, val comments: List<String>? = null)
// Actions
object Load
class SetComments(val comments: List<String>?)
val repository: CommentRepositoryImpl // get repository somewhere e.g. manually create, DI, or 3rd party library
val store = Store(
scope = viewScope,
initialState = CommentsState(),
reducers = mapOf(
SetComments::class to Reducer { currentState: CommentsState, action: SetComments -> // This reducer is connected with SetComments action by using SetComments::class as a Key
currentState.copy(comments = action.comments)
}
),
middlewares = mapOf(
Load::class to Middleware { _: Order, store: Store, state: CommentsState, _: Load -> // This middleware is connected with Load action by using Load::class as a Key
if (state.isLoading) return@Middleware
scope.launch {
val result = repository.getComments()
if (result.isSuccess) {
store.dispatch(SetComments(result.value))
} else {
store.dispatch(SetComments(null))
}
}
}
)
)
Usage
// in Coroutine scope - you can observe state changes with `collect`
store.states
.collect {
/** This should return { comments : [ {
"postId": 1,
"id": 1,
"name": "id labore ex et quam laborum",
"email": "[email protected]",
"body": "laudantium enim quasi est quidem magnam voluptate ipsam eos\ntempora quo necessitatibus\ndolor quam autem quasi\nreiciendis et nam sapiente accusantium"
}, ... ] }
**/
println(it)
}
// dispatch an action
store.dispatch(Load)
or you can use the simplified store creation version like;
val store = Store(
scope = viewScope,
initialState = CommentsState(),
reducers = setOf(
reducer { currentState: CommentsState, action: SetComments -> // This reducer is connected with SetComments action by using SetComments::class as a Key
currentState.copy(comments = action.comments)
}
),
middlewares = setOf(
middleware { _: Order, store: Store, state: CommentsState, _: Load -> // This middleware is connected with Load action by using Load::class as a Key
if (state.isLoading) return@Middleware
scope.launch {
val result = repository.getComments()
if (result.isSuccess) {
store.dispatch(SetComments(result.value))
} else {
store.dispatch(SetComments(null))
}
}
}
)
)
It uses the reified function with T::class
for action type like action: SetComments
or _: Load
automatically under the hood.
For documentation, check more details in the README
For example, check more tests in the commonTest folder.
CoRed is brought to you by contributors.
CoRed is released under the MIT license.