Skip to content

Commit

Permalink
Hook(react): useReducer (#11)
Browse files Browse the repository at this point in the history
* feat: useReducer

* test: useReducer

* demo: useReducer
  • Loading branch information
pavi2410 authored Feb 20, 2022
1 parent 6201c8b commit e74deb2
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ sealed class ExampleScreen(
) {
object Counter : ExampleScreen("counter", "Counter", { CounterExample() })
object Context : ExampleScreen("context", "Context", { ContextExample() })
object Reducer : ExampleScreen("reducer", "Reducer", { ReducerExample() })
object Network : ExampleScreen("network", "Network", { NetworkExample() })
}

val exampleScreens
get() = listOf(
ExampleScreen.Counter,
ExampleScreen.Context,
ExampleScreen.Reducer,
ExampleScreen.Network
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package me.pavi2410.useCompose.app.screens

import androidx.compose.foundation.layout.Column
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import me.pavi2410.useCompose.react.useReducer

data class MyState(val count: Int)
data class MyAction(val type: String)

val initialState = MyState(0)

@Composable
fun ReducerExample() {
Column {
val (state, dispatch) = useReducer<MyState, MyAction>(initialState) { state, action ->
when (action.type) {
"increment" -> state.copy(count = state.count + 1)
"decrement" -> state.copy(count = state.count - 1)
else -> throw Error()
}
}

Text("Count: ${state.count}")
Button(onClick = {
dispatch(MyAction("increment"))
}) {
Text("+")
}
Button(onClick = {
dispatch(MyAction("decrement"))
}) {
Text("-")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,39 @@ class HooksTest {
composeTestRule.onNodeWithTag("outer").assertTextEquals("context value is outer")
composeTestRule.onNodeWithTag("inner").assertTextEquals("context value is inner")
}
}

@Test
fun useReducerTest() {
val initialState = MyState(0)

// Start the app
composeTestRule.setContent {
Surface(color = MaterialTheme.colors.background) {
Column {
val (state, dispatch) = useReducer<MyState, MyAction>(initialState) { state, action ->
when (action.type) {
"increment" -> state.copy(count = state.count + 1)
"decrement" -> state.copy(count = state.count - 1)
else -> throw Error()
}
}

Text("Count: ${state.count}")
Button(onClick = {
dispatch(MyAction("increment"))
}) {
Text("+")
}
Button(onClick = {
dispatch(MyAction("decrement"))
}) {
Text("-")
}
}
}
}
}
}

data class MyState(val count: Int)
data class MyAction(val type: String)
18 changes: 17 additions & 1 deletion react/src/main/java/me/pavi2410/useCompose/react/hooks.kt
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,26 @@ fun <T> createContext(initialValue: T): ReactContext<T> {
/**
* A React-ish hook that returns the current value for that context.
*
* @see [useEffect](https://reactjs.org/docs/hooks-effect.html)
* @see [useContext](https://reactjs.org/docs/hooks-reference.html#usecontext)
*/
@SuppressLint("ComposableNaming")
@Composable
fun <T> useContext(context: ReactContext<T>): T {
return context.LocalCtx.current
}

/**
* A hook from React that allows you to create a state machine using a [reducer] function.
*
* @see [useReducer](https://reactjs.org/docs/hooks-reference.html#usereducer)
*/
@Composable
fun <S, A> useReducer(initialState: S, reducer: (S, A) -> S): Pair<S, (A) -> Unit> {
var thisState by remember { mutableStateOf(initialState) }

fun dispatch(action: A) {
thisState = reducer(thisState, action)
}

return Pair(thisState, ::dispatch)
}

0 comments on commit e74deb2

Please # to comment.