ReduxSimple
Simple Stupid Redux Store using Reactive Extensions
Install / Use
/learn @Odonno/ReduxSimpleREADME

Redux Simple
| Package | Versions |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| ReduxSimple | |
| ReduxSimple.Entity |
|
| ReduxSimple.Uwp |
|
| ReduxSimple.Uwp.RouterStore |
|
| ReduxSimple.Uwp.DevTools |
|
Simple Stupid Redux Store using Reactive Extensions
Redux Simple is a .NET library based on Redux principle. Redux Simple is written with Rx.NET and built with the minimum of code you need to scale your whatever .NET application you want to design.
Example app
There is a sample UWP application to show how ReduxSimple library can be used and the steps required to make a C#/XAML application using the Redux pattern.
You can follow this link: https://www.microsoft.com/store/apps/9PDBXGFZCVMS
Getting started
Like the original Redux library, you will have to initialize a new State when creating a Store + you will create Reducer functions each linked to an Action which will possibly update this State.
In your app, you can:
DispatchnewActionto change theState- and listen to events/changes using the
Subscribemethod
You will need to follow the following steps to create your own Redux Store:
- Create
Statedefinition
public record RootState
{
public string CurrentPage { get; set; } = string.Empty;
public ImmutableArray<string> Pages { get; set; } = ImmutableArray<string>.Empty;
}
Each State should be immutable. That's why we prefer to use immutable types for each property of the State.
- Create
Actiondefinitions
public class NavigateAction
{
public string PageName { get; set; }
}
public class GoBackAction { }
public class ResetAction { }
- Create
Reducerfunctions
public static class Reducers
{
public static IEnumerable<On<RootState>> CreateReducers()
{
return new List<On<RootState>>
{
On<NavigateAction, RootState>(
(state, action) => state with { Pages = state.Pages.Add(action.PageName) }
),
On<GoBackAction, RootState>(
state =>
{
var newPages = state.Pages.RemoveAt(state.Pages.Length - 1);
return state with {
CurrentPage = newPages.LastOrDefault(),
Pages = newPages
};
}
),
On<ResetAction, RootState>(
state => state with {
CurrentPage = string.Empty,
Pages = ImmutableArray<string>.Empty
}
)
};
}
}
- Create a new instance of your Store
sealed partial class App
{
public static readonly ReduxStore<RootState> Store;
static App()
{
Store = new ReduxStore<RootState>(CreateReducers());
}
}
- And be ready to use your store inside your entire application...
Features
<details> <summary>Dispatch & Subscribe</summary> <br>You can now dispatch new actions using your globally accessible Store.
using static MyApp.App; // static reference on top of your file
Store.Dispatch(new NavigateAction { PageName = "Page1" });
Store.Dispatch(new NavigateAction { PageName = "Page2" });
Store.Dispatch(new GoBackAction());
And subscribe to either state changes or actions raised.
using static MyApp.App; // static reference on top of your file
Store.ObserveAction<NavigateAction>().Subscribe(_ =>
{
// TODO : Handle navigation
});
Store.Select(state => state.CurrentPage)
.Where(currentPage => currentPage == nameof(Page1))
.UntilDestroyed(this)
.Subscribe(_ =>
{
// TODO : Handle event when the current page is now "Page1"
});
</details>
<details>
<summary>Reducers</summary>
<br>
Reducers are pure functions used to create a new state once an action is triggered.
Reducers on action
You can define a list of On functions where at least one action can be triggered.
return new List<On<RootState>>
{
On<NavigateAction, RootState>(
(state, action) => state with { Pages = state.Pages.Add(action.PageName) }
),
On<GoBackAction, RootState>(
state =>
{
var newPages = state.Pages.RemoveAt(state.Pages.Length - 1);
return state with {
CurrentPage = newPages.LastOrDefault(),
Pages = newPages
};
}
),
On<ResetAction, RootState>(
state => state with {
CurrentPage = string.Empty,
Pages = ImmutableArray<string>.Empty
}
)
};
Sub-reducers aka feature reducers
Sub-reducers also known as feature reducers are nested reducers that are used to update a part of the state. They are mainly used in larger applications to split state and reducer logic in multiple parts.
The CreateSubReducers function helps you to create sub-reducers. This function has a few requirements:
- a
Selector- to be able to access the value of the current nested state - a
Reducer- to explicitly detail how to update the parent state given a new value for the nested state - and the list of reducers using
Onpattern
First you need to create a new state lens for feature/nested states:
public static IEnumerable<On<RootState>> GetReducers()
{
return CreateSubReducers(SelectCounterState)
.On<IncrementAction>(state => state with { Count = state.Count + 1 })
.On<DecrementAction>(state => state with { Count = state.Count - 1 })
.ToList();
}
Then you can combine nested reducers into your root state:
public static IEnumerable<On<RootState>> CreateReducers()
{
return CombineReducers(
Counter.Reducers.GetReducers(),
TicTacToe.Reducers.GetReducers(),
TodoList.Reducers.GetReducers(),
Pokedex.Reducers.GetReducers()
);
}
And so inject your reducers into the Store:
public static readonly ReduxStore<RootState> Store =
new ReduxStore<RootState>(CreateReducers(), RootState.InitialState);
Remember that following this pattern, you can have an infinite number of layers for your state.
</details> <details> <summary>Selectors</summary> <br>Based on what you need, you can observe the entire state or just a part of it.
Note that every selector is a memoized selector by design, which means that a next value will only be subscribed if there is a difference with the previous value.
Full state
Store.Select()
.Subscribe(state =>
{
// Listening to the full state (when any property changes)
});
Inline function
You can use functions to select a part of the state, like this:
Store.Select(state => state.CurrentPage)
.Subscribe(currentPage =>
{
// Listening to the "CurrentPage" property of the state (when only this property changes)
});
Simple selectors
Simple selectors are like functions but the main benefits are that they can be reused in multiple components and they can be reused to create other selectors.
public static ISelectorWithoutProps<RootState, string> SelectCurrentPage = CreateSelector(
(RootState state) => state.CurrentPage
);
public static ISelectorWithoutProps<RootState, ImmutableArray<string>> SelectPages = CreateSelector(
(RootState state) => state.Pages
);
Store.Select(SelectCurrentPage)
.Subscribe(currentPage =>
{
// Listening to the "CurrentPage" property of the state (when only this property changes)
});
Reuse selectors - without props
Note that you can combine multiple selectors to create a new one.
public static ISelectorWithoutProps<RootState, bool> SelectHasPreviousPage = CreateSelector(
SelectPages,
(ImmutableArray<string> pages) => pages.Count() > 1
);
Reuse selectors - with props
You can also use variables out of the store to create a new selector.
public static ISelectorWithProps<RootState, string, bool> SelectIsPageSelected = CreateSelector(
SelectCurrentPage,
(string currentPage, string selectedPage) => currentPage == selectedPage
);
And then use it this way:
Store.Select(SelectIsPageSelected, "mainPage")
.Subscribe(isMainPageSelected =>
{
// TODO
});
Combine selectors
Sometimes, you need to consume multiple selectors. In some cases, you just want to combine them. This is what you can do with CombineSelectors function. Here is an example:
Store.Select(
CombineSelectors(SelectGameEnded, SelectWinner)
)
.Subscribe(x =>
{
var (gameEnded, winner) = x;
// TODO
});
</details>
<details>
<summary>Effects - Asynchronous Actions</summary>
<br>
Sid
