-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from le-nn/feature/support_redux_devtools
support redux devtools and adjusts namespace (Fix #4)
- Loading branch information
Showing
39 changed files
with
812 additions
and
299 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,186 +1,189 @@ | ||
# Memento | ||
|
||
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) | ||
|
||
Easy unidirectional store and redo/undo library for state management for frontend apps on Blazor/.NET | ||
|
||
# Basic Concept | ||
|
||
We provides a Store that allows you to share state between components. | ||
All stores are managed by a single provider and can subscribe to state change notifications. | ||
Undirectional flow and immutable change of state provides a predictable architecture. | ||
In addition, we provide a store that easily implements Redo/Undo by managing in immutable states. | ||
|
||
### For patterns like Flux or MVU | ||
|
||
Besides simple store pattern, we also provide patterns inspired by MVU patterns such as Flux and Elm. | ||
Since you should change the state via the Reducer, you can change the state based on stricter rules and observe the state in detail. | ||
|
||
### Devtool | ||
|
||
[Redux DevTools](https://github.com/reduxjs/redux-devtools) is supported. | ||
Redux DevTools is a tool for debugging application's state changes. | ||
State can be time traveled and history can be viewed in DevTools. | ||
|
||
[See documentation](./docs/DevTools.md) for details of usage. | ||
|
||
## DEMO Page | ||
|
||
https://le-nn.github.io/memento/ | ||
|
||
If you have ReduxDevTool installed, | ||
DevTool will launch automatically. | ||
You can do state history and time travel. | ||
|
||
## React or TS/JS bindings | ||
|
||
Currently, moved to here | ||
https://github.com/le-nn/memento-js | ||
|
||
## Features | ||
|
||
* Less boilarplate, less rule and simple usage | ||
* Immutable and Unidirectional data flow | ||
* Multiple stores but manged by single provider, so can observe and manage as one state tree | ||
* Observe detailed status with command patterns and makes it easier to monitor what happened within the application | ||
|
||
## Concepts and Data Flow | ||
|
||
<img width="800px" src="./Architecture.jpg"/> | ||
|
||
## Rules | ||
|
||
* State should always be read-only. | ||
* The UI then uses the new state to render its display. | ||
|
||
### For patterns like Flux | ||
* Every Reducer that processes in the action will create new state to reflect the old state combined with the changes expected for the action. | ||
* To change state our app should Dispatch via Reducer in the action method | ||
|
||
## Overview | ||
|
||
This is an C# and Blazor example that implements counter. | ||
|
||
Simple Store Pattern | ||
|
||
```csharp | ||
using Memento.Core; | ||
using System.Collections.Immutable; | ||
|
||
namespace Memento.Sample.Blazor; | ||
|
||
public record AsyncCounterState { | ||
public int Count { get; init; } = 0; | ||
|
||
public bool IsLoading { get; init; } = false; | ||
|
||
public ImmutableArray<int> Histories { get; init; } = ImmutableArray.Create<int>(); | ||
} | ||
|
||
public class AsyncCounterStore : Store<AsyncCounterStore> { | ||
public AsyncCounterStore() : base(() => new()) { } | ||
|
||
public async Task CountUpAsync() { | ||
Mutate(state => state with { IsLoading = true, }); | ||
await Task.Delay(800); | ||
Mutate(state => state with { | ||
IsLoading = false, | ||
Count = state.Count + 1, | ||
Histories = state.Histories.Add(state.Count + 1), | ||
}); | ||
} | ||
} | ||
|
||
``` | ||
|
||
Flux Store Pattern | ||
```csharp | ||
using Memento.Core; | ||
using System.Collections.Immutable; | ||
using static Memento.Sample.Blazor.Stores.AsyncCounterCommands; | ||
|
||
namespace Memento.Sample.Blazor; | ||
|
||
public record AsyncCounterState { | ||
public int Count { get; init; } = 0; | ||
|
||
public bool IsLoading { get; init; } = false; | ||
|
||
public ImmutableArray<int> Histories { get; init; } = ImmutableArray.Create<int>(); | ||
} | ||
|
||
public record AsyncCounterCommands: Command { | ||
public record Increment : AsyncCounterCommands; | ||
public record SetCount(int Count) : AsyncCounterCommands; | ||
public record BeginLoading : AsyncCounterCommands; | ||
} | ||
|
||
public class AsyncCounterStore : FluxStore<AsyncCounterState, AsyncCounterCommands> { | ||
public AsyncCounterStore() : base(() => new(), Reducer) { } | ||
|
||
static AsyncCounterState Reducer(AsyncCounterState state, AsyncCounterCommands command) { | ||
return command switch { | ||
CountUp => state with { | ||
Count = state.Count + 1, | ||
IsLoading = false, | ||
Histories = state.Histories.Add(state.Count + 1), | ||
}, | ||
BeginLoading => state with { | ||
IsLoading = true, | ||
}, | ||
_ => throw new CommandNotHandledException(command), | ||
}; | ||
} | ||
|
||
public async Task CountUpAsync() { | ||
Dispatch(new BeginLoading()); | ||
await Task.Delay(800); | ||
Dispatch(new CountUp()); | ||
} | ||
} | ||
|
||
``` | ||
|
||
Razor view | ||
```razor | ||
@page "/counter" | ||
@inherits ObserverComponet | ||
@inject AsyncCounterStore AsyncCounterStore | ||
<PageTitle>Counter</PageTitle> | ||
<h1>Async Counter</h1> | ||
<p role="status">Current count: @AsyncCounterStore.State.Count</p> | ||
<p role="status">Loading: @AsyncCounterStore.State.IsLoading</p> | ||
<p role="status" class="mb-0">History</p> | ||
<div class="d-flex"> | ||
[ | ||
@foreach (var item in string.Join(", ", AsyncCounterStore.State.Histories)) { | ||
@item | ||
} | ||
] | ||
</div> | ||
<button class="mt-3 btn btn-primary" @onclick="IncrementCount">Count up</button> | ||
@code { | ||
async Task IncrementCount() { | ||
await AsyncCounterStore.CountUpAsync(); | ||
} | ||
} | ||
``` | ||
|
||
## Compatibility and bindings | ||
|
||
| Package Name | Version | Lang | Platform | Package manager | Release Notes | Package provider | | ||
| --------------- | ------- | ---------- | ------------------- | --------------- | ---------------------------------- | ------------------------------------------------------ | | ||
| Memento.Core | 1.0.0 | C# | .NET 6 or later | NuGet | [Notes](./release-notes.dotnet.md) | [NuGet](https://www.nuget.org/packages/Memento.Core) | | ||
| Memento.Blazor | 1.0.0 | Blazor | .NET 6 or later | NuGet | [Notes](./release-notes.dotnet.md) | [NuGet](https://www.nuget.org/packages/Memento.Blazor) | | ||
|
||
# Documentation | ||
|
||
[Basic Concept with C#](./docs/Tutorial.cs.md) | ||
|
||
[Blazor](./docs/Blazor/GettingStandard.md) | ||
|
||
# License | ||
Designed with ♥ by le-nn. Licensed under the MIT License. | ||
# Memento | ||
|
||
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) | ||
|
||
Easy unidirectional store and redo/undo library for state management for frontend apps on Blazor/.NET | ||
|
||
# Basic Concept | ||
|
||
We provides a Store that allows you to share state between components. | ||
All stores are managed by a single provider and can subscribe to state change notifications. | ||
Undirectional flow and immutable change of state provides a predictable architecture. | ||
In addition, we provide a store that easily implements Redo/Undo by managing in immutable states. | ||
|
||
### For patterns like Flux or MVU | ||
|
||
Besides simple store pattern, we also provide patterns inspired by MVU patterns such as Flux and Elm. | ||
Since you should change the state via the Reducer, you can change the state based on stricter rules and observe the state in detail. | ||
|
||
### Devtool | ||
|
||
[Redux DevTools](https://github.com/reduxjs/redux-devtools) is supported. | ||
Redux DevTools is a tool for debugging application's state changes. | ||
State can be time traveled and history can be viewed in DevTools. | ||
|
||
[See documentation](./docs\ReduxDevTool.md) for details of usage. | ||
|
||
## DEMO Page | ||
|
||
https://le-nn.github.io/memento/ | ||
|
||
If you have ReduxDevTool installed, | ||
DevTool will launch automatically. | ||
You can do state history and time travel. | ||
|
||
## React or TS/JS bindings | ||
|
||
Currently, moved to here | ||
https://github.com/le-nn/memento-js | ||
|
||
## Features | ||
|
||
* Less boilerplate, less rule and simple usage | ||
* Immutable state and Unidirectional flow | ||
* Multiple stores but manged by single provider, so can observe and manage as one state tree | ||
* Observe detailed status with command patterns and makes it easier to monitor what happened within the application | ||
|
||
## Concepts and Flow | ||
|
||
<img width="800px" src="./Architecture.jpg"/> | ||
|
||
## Rules | ||
|
||
* State should always be read-only. | ||
* The UI then uses the new state to render its display. | ||
|
||
### For patterns like Flux | ||
* Every Reducer that processes in the action will create new state to reflect the old state combined with the changes expected for the action. | ||
* To change state our app should Dispatch via Reducer in the action method | ||
|
||
## Overview | ||
|
||
This is an C# and Blazor example that implements counter. | ||
|
||
Simple Store Pattern | ||
|
||
```csharp | ||
using Memento.Core; | ||
using System.Collections.Immutable; | ||
|
||
namespace Memento.Sample.Blazor; | ||
|
||
public record AsyncCounterState { | ||
public int Count { get; init; } = 0; | ||
|
||
public bool IsLoading { get; init; } = false; | ||
|
||
public ImmutableArray<int> Histories { get; init; } = ImmutableArray.Create<int>(); | ||
} | ||
|
||
public class AsyncCounterStore : Store<AsyncCounterStore> { | ||
public AsyncCounterStore() : base(() => new()) { } | ||
|
||
public async Task CountUpAsync() { | ||
Mutate(state => state with { IsLoading = true, }); | ||
await Task.Delay(800); | ||
Mutate(state => state with { | ||
IsLoading = false, | ||
Count = state.Count + 1, | ||
Histories = state.Histories.Add(state.Count + 1), | ||
}); | ||
} | ||
} | ||
|
||
``` | ||
|
||
Flux Store Pattern | ||
```csharp | ||
using Memento.Core; | ||
using System.Collections.Immutable; | ||
using static Memento.Sample.Blazor.Stores.AsyncCounterCommands; | ||
|
||
namespace Memento.Sample.Blazor; | ||
|
||
public record AsyncCounterState { | ||
public int Count { get; init; } = 0; | ||
|
||
public bool IsLoading { get; init; } = false; | ||
|
||
public ImmutableArray<int> Histories { get; init; } = ImmutableArray.Create<int>(); | ||
} | ||
|
||
public record AsyncCounterCommands: Command { | ||
public record Increment : AsyncCounterCommands; | ||
public record SetCount(int Count) : AsyncCounterCommands; | ||
public record BeginLoading : AsyncCounterCommands; | ||
} | ||
|
||
public class AsyncCounterStore : FluxStore<AsyncCounterState, AsyncCounterCommands> { | ||
public AsyncCounterStore() : base(() => new(), Reducer) { } | ||
|
||
static AsyncCounterState Reducer(AsyncCounterState state, AsyncCounterCommands command) { | ||
return command switch { | ||
CountUp => state with { | ||
Count = state.Count + 1, | ||
IsLoading = false, | ||
Histories = state.Histories.Add(state.Count + 1), | ||
}, | ||
BeginLoading => state with { | ||
IsLoading = true, | ||
}, | ||
_ => throw new CommandNotHandledException(command), | ||
}; | ||
} | ||
|
||
public async Task CountUpAsync() { | ||
Dispatch(new BeginLoading()); | ||
await Task.Delay(800); | ||
Dispatch(new CountUp()); | ||
} | ||
} | ||
|
||
``` | ||
|
||
Razor view | ||
```razor | ||
@page "/counter" | ||
@inherits ObserverComponet | ||
@inject AsyncCounterStore AsyncCounterStore | ||
<PageTitle>Counter</PageTitle> | ||
<h1>Async Counter</h1> | ||
<p role="status">Current count: @AsyncCounterStore.State.Count</p> | ||
<p role="status">Loading: @AsyncCounterStore.State.IsLoading</p> | ||
<p role="status" class="mb-0">History</p> | ||
<div class="d-flex"> | ||
[ | ||
@foreach (var item in string.Join(", ", AsyncCounterStore.State.Histories)) { | ||
@item | ||
} | ||
] | ||
</div> | ||
<button class="mt-3 btn btn-primary" @onclick="IncrementCount">Count up</button> | ||
@code { | ||
async Task IncrementCount() { | ||
await AsyncCounterStore.CountUpAsync(); | ||
} | ||
} | ||
``` | ||
|
||
## Compatibility and bindings | ||
|
||
| Package Name | Platform | Desctiption | | ||
| ------------------------------------------------------------------------------------------- | ------------------- | ----------------------------------------------------------- | | ||
| [Memento.Core](https://www.nuget.org/packages/Memento.Core) | .NET 6 or later | Core Package of Memento | | ||
| [Memento.Blazor](https://www.nuget.org/packages/Memento.Blazor) | .NET 6 or later | Provides Observing state changes on Blazor Component. | | ||
| [Memento.ReduxDevTool.Remote](https://www.nuget.org/packages/Memento.ReduxDevTool.Remote) | .NET 6 or later | Connect and Interact with applications via WebSocket. | | ||
| [Memento.ReduxDevTool.Browser](https://www.nuget.org/packages/Memento.ReduxDevTool.Browser) | .NET 6 or later | Interact with ReduxDevTools via JavaScript interop. | | ||
| [Memento.ReduxDevTool](https://www.nuget.org/packages/Memento.ReduxDevTool) | .NET 6 or later | Provides basic functionality to interact with ReduxDevTools. Interop is required. | | ||
|
||
# Documentation | ||
|
||
[Basic Concept with C#](./docs/Tutorial.cs.md) | ||
|
||
[Blazor](./docs/Blazor/GettingStandard.md) | ||
|
||
# License | ||
Designed with ♥ by le-nn. Licensed under the MIT License. |
Oops, something went wrong.