SkillAgentSearch skills...

ReduxSimple

Simple Stupid Redux Store using Reactive Extensions

Install / Use

/learn @Odonno/ReduxSimple

README

./images/logo.png

Redux Simple

CodeFactor

| Package | Versions | | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | ReduxSimple | NuGet | | ReduxSimple.Entity | NuGet | | ReduxSimple.Uwp | NuGet | | ReduxSimple.Uwp.RouterStore | NuGet | | ReduxSimple.Uwp.DevTools | NuGet |

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:

  • Dispatch new Action to change the State
  • and listen to events/changes using the Subscribe method

You will need to follow the following steps to create your own Redux Store:

  1. Create State definition
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.

  1. Create Action definitions
public class NavigateAction
{
    public string PageName { get; set; }
}

public class GoBackAction { }

public class ResetAction { }
  1. Create Reducer functions
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
                }
            )
        };
    }
}
  1. Create a new instance of your Store
sealed partial class App
{
    public static readonly ReduxStore<RootState> Store;

    static App()
    {
        Store = new ReduxStore<RootState>(CreateReducers());
    }
}
  1. 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 On pattern

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

View on GitHub
GitHub Stars140
CategoryDevelopment
Updated2mo ago
Forks20

Languages

C#

Security Score

100/100

Audited on Jan 5, 2026

No findings