Coder Social home page Coder Social logo

faogustavo / fluks Goto Github PK

View Code? Open in Web Editor NEW
3.0 3.0 1.0 110 KB

Implementation of flux application architecture on top of Kotlin Coroutines for multiplatform projects.

License: Apache License 2.0

Kotlin 100.00%
flux kotlin hacktoberfest kmm kmp multiplatform-kotlin-library kotlin-common

fluks's Introduction

Fluks

Bintray License GitHub issues

GitHub top language GitHub top language GitHub top language

Implementation of flux application architecture on top of Kotlin Coroutines for multiplatform projects.

// Define your state
data class State(
    val count: Int
) : Fluks.State

// Define your actions
sealed class Action : Fluks.Action {
    object Inc : Action()
    object Dec : Action()
    class Mult(val multiplier: Int) : Action()
    class Div(val divider: Int) : Action()
}

// Create your store
private val store: Fluks.Store<State> = store(
    initialValue = State(0),
    reducer = reducer { state, action ->
        when(action) {
            is Action.Inc -> state.copy(
                 count = state.count + 1
            )
            is Action.Dec -> state.copy(
                count = state.count - 1
            )
            is Action.Mult -> state.copy(
                count = state.count * action.multiplier
            )
            is Action.Div -> state.copy(
                count = state.count / action.divider
            )
            else -> state
        }
    },
)

// Dispatch your actions
store.dispatch(Action.Inc)
store.dispatch(Action.Dec)
store.dispatch(Action.Mult(2))
store.dispatch(Action.Div(2))

// Use the state
val currentState = store.value
store.valueFlow
    .onEach { state -> /* do something */ }
    .launchIn(scope) 

Installation

Add this implementation to you gradle file:

implementation "dev.valvassori.fluks:core:$fluks_version"

Usage

1. Create your state class inheriting from Fluks.State ;

data class State(
    val count: Int
) : Fluks.State

2. Create your actions. They have to inherit from Fluks.Action;

sealed class Action : Fluks.Action {
    object Inc : Action()
    object Dec : Action()
    class Mult(val multiplier: Int) : Action()
    class Div(val divider: Int) : Action()
}

3. Create your store inheriting from Fluks.Store and implement the abstract methods;

In this step, you can opt for two variants.

Inherit from AbstractStore;

private class Store : AbstractStore<State>() {
    override val initialValue: State
        get() = State(count = 0)

    override fun reduce(currentState: State, action: Fluks.Action): State =
        when (action) {
            is Action.Inc -> currentState.copy(
                count = currentState.count + 1
            )
            is Action.Dec -> currentState.copy(
                count = currentState.count - 1
            )
            is Action.Mult -> currentState.copy(
                count = currentState.count * action.multiplier
            )
            is Action.Div -> currentState.copy(
                count = currentState.count / action.divider
            )
            else -> currentState
        }
}

val store = Store()

or use the store helper function

val store: Fluks.Store<State> = store(
    initialValue = State(0),
    reducer = reducer { state, action ->
        when (action) {
            is Action.Inc -> state.copy(
                count = state.count + 1
            )
            is Action.Dec -> state.copy(
                count = state.count - 1
            )
            is Action.Mult -> state.copy(
                count = state.count * action.multiplier
            )
            is Action.Div -> state.copy(
                count = state.count / action.divider
            )
            else -> state
        }
    },
)

As you can see, in both of them, you must provide an initialValue and a reducer.

4. (Optional) Create your reducer

When you are using the store helper function, you can create reducer apart from the function call to improve readability and make it easier to test.

// You can also use the `Reducer` fun interface with the capital 'R'.
val storeReducer = reducer { currentState, action -> 
    when(action) {
        is Action.Inc -> state.copy(
             count = currentState.count + 1
        )
        is Action.Dec -> currentState.copy(
            count = currentState.count - 1
        )
        is Action.Mult -> currentState.copy(
            count = currentState.count * action.multiplier
        )
        is Action.Div -> currentState.copy(
            count = currentState.count / action.divider
        )
        else -> state
    }
}

5. Dispatch your actions to the store using the .dispatch(Fluks.Action) method from the store;

After having your store instance, you can dispatch your actions.

store.dispatch(Action.Inc)
store.dispatch(Action.Dec)
store.dispatch(Action.Mult(2))
store.dispatch(Action.Div(2))

6. (Optional) Adding middlewares

When required, you can add middlewares to help you update some dispatched action. The middlewares are executed in a chain, and the last node is the reducer.

To add a new middleware, create a new one using the Middleware fun interface. Then, implement the lambda with the three required parameters and return the updated state:

  • Store: The store that dispatched the action
  • Action: The action that has been dispatched
  • Next: The next node from the chain
val stateLogMiddleware = Middleware<State> { store, action, next ->
    val messages = mutableListOf(
        "[Old State]: ${store.value.count}",
        "[Action]: ${action::class.simpleName}",
    )

    val updatedState = next(action)

    messages.add("[New State]: ${updatedState.count}")
    messages.forEach { logger.log(it) }

    updatedState
}

After having an instance of your middleware, apply it to the store that you need.

// For one middleware only
store.applyMiddleware(stateLogMiddleware)

// For multiple middlewares
store.applyMiddleware(listOf(stateLogMiddleware))

If you already have a created chain of middlewares, you can just add a new one to it by calling addMiddleware(middleware).

store.addMiddleware(stateLogMiddleware)

Be careful with the applyMiddleware function if you already declared your middlewares. Each time you call this function, you create a new chain and overwrites the previous one. If you just want to add a new node, use the addMiddleware function.

Global Dispatcher

In some scenarios, you will need to dispatch an action to all of your stores (like a logout to clear the user content). If this is the case, we have a global function called dispatch(Fluks.Action) that receives and action and calls all your stores.

object Logout : Fluks.Action

val accountStore = store(emptyAccountState, accountReducer)
val ordersStore = store(emptyOrdersState, ordersReducer)

dispatch(Logout)

assertFalse(accountStore.value.isUserLoggedIn)
assertTrue(ordersStore.value.orders.isEmpty())

Combined stores

If you have more than one store, and you need to combine them to generate a new state, you can use the AbstractCombinedStore. Using it, you need to provide the stores that you depends on, and implement the combine function.

data class State0(val count0: Int) : Fluks.State
data class State1(val count1: Int) : Fluks.State
data class StateOut(val multiplication: Int) : Fluks.State

val store0 = store(State0(count0 = 1), reducer0)
val store1 = store(State1(count1 = 1), reducer1)

val combinedStores = combineStores(
    initialValue = StateOut(multiplication = 1),
    store0 = store0,
    store1 = store1,
    baseContext = Dispatchers.Main
) { s0, s1 -> StateOut(multiplication = s0.count0 * s1.count1) }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.