{"id":13808206,"url":"https://github.com/le-nn/memento","last_synced_at":"2026-02-15T09:16:40.605Z","repository":{"id":44434371,"uuid":"324639899","full_name":"le-nn/memento","owner":"le-nn","description":"A simple client-side state management container for Blazor/.NET includes redo/undo and ReduxDevTools support.","archived":false,"fork":false,"pushed_at":"2024-02-19T06:24:31.000Z","size":29486,"stargazers_count":43,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-10T00:52:02.571Z","etag":null,"topics":["blazor","flux","redux-devtools","state-management","store"],"latest_commit_sha":null,"homepage":"https://github.com/le-nn/memento","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/le-nn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-12-26T21:43:57.000Z","updated_at":"2025-04-08T03:56:14.000Z","dependencies_parsed_at":"2023-12-17T22:42:15.276Z","dependency_job_id":"87164707-44d6-498f-be72-b418af398da0","html_url":"https://github.com/le-nn/memento","commit_stats":{"total_commits":188,"total_committers":2,"mean_commits":94.0,"dds":"0.29255319148936165","last_synced_commit":"640a1d4a34c818778cb993e0ca58ee605ec905e8"},"previous_names":["rempei-okada/relux.js"],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/le-nn%2Fmemento","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/le-nn%2Fmemento/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/le-nn%2Fmemento/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/le-nn%2Fmemento/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/le-nn","download_url":"https://codeload.github.com/le-nn/memento/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248137996,"owners_count":21053775,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blazor","flux","redux-devtools","state-management","store"],"created_at":"2024-08-04T01:01:37.336Z","updated_at":"2026-02-15T09:16:40.573Z","avatar_url":"https://github.com/le-nn.png","language":"C#","funding_links":[],"categories":["Libraries \u0026 Extensions"],"sub_categories":["Tools \u0026 Utilities"],"readme":"# Memento\r\n\r\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\r\n\r\nA simple client-side state management container for Blazor/.NET includes redo/undo and ReduxDevTools support.\r\n\r\n\u003cimg src=\"./docs/Assets/ReduxDevTools.gif\" width=\"800px\" /\u003e\r\n\r\n### Japanese Link\r\n\r\n[使い方と紹介](https://zenn.dev/remrem/articles/32ee38e79f4cf0)\r\n\r\n[Redux DevToolsについて](https://zenn.dev/remrem/articles/0768982b3cdc92)\r\n\r\n# Basic Concept\r\n\r\nWe provides a Store that allows you to share state between components.\r\nAll stores are managed by a single provider and can subscribe to state change notifications.\r\nUndirectional flow and immutable change of state provides a predictable architecture.\r\nIn addition, we provide a store that easily implements Redo/Undo by managing in immutable states.\r\n\r\n### For patterns like Flux or MVU\r\n\r\nBesides simple store pattern, we also provide patterns inspired by MVU patterns such as Flux and Elm.\r\nSince you should change the state via the Reducer, you can change the state based on stricter rules and observe the state in detail.\r\n\r\n### ReduxDevTools\r\n\r\n[Redux DevTools](https://github.com/reduxjs/redux-devtools) is supported.\r\nRedux DevTools is a tool for debugging application's state changes.\r\nState can be time traveled and history can be viewed in DevTools.\r\n\r\n[See docs](./docs/ReduxDevTools.md) for details of usage.\r\n\r\n## DEMO Page\r\n\r\nhttps://le-nn.github.io/memento/\r\n\r\nIf you have ReduxDevTool installed,\r\nDevTool will launch automatically.\r\nYou can do state history and time travel.\r\n\r\n# Documentation\r\n\r\nhttps://le-nn.github.io/memento/docs/README.html\r\n\r\n## React or TS/JS bindings\r\n\r\nCurrently, moved to here\r\nhttps://github.com/le-nn/memento-js\r\n\r\n## Features\r\n\r\n* Less boilerplate, less rule and simple usage \r\n* Immutable state and Unidirectional flow\r\n* Multiple stores but managed by single provider, so can observe and manage as one state tree\r\n* Observe detailed status with command patterns and makes it easier to monitor what happened within the application \r\n\r\n## Concepts and Flow\r\n\r\n\u003cimg width=\"800px\" src=\"./Architecture.jpg\"/\u003e\r\n\r\n## Rules\r\n\r\n* State should always be read-only.\r\n* The UI then uses the new state to render its display.\r\n\r\n### For patterns like Flux\r\n* Every Reducer that processes in the action will create new state to reflect the old state combined with the changes expected for the action.\r\n* To change state our app should Dispatch via Reducer in the action method\r\n\r\n## Store Overview\r\n\r\nThis is an C# and Blazor example that implements counter.\r\n\r\nSimple Store Pattern\r\n\r\n```csharp\r\nusing Memento.Core;\r\nusing System.Collections.Immutable;\r\n\r\nnamespace Memento.Sample.Blazor;\r\n\r\npublic record AsyncCounterState {\r\n    public int Count { get; init; } = 0;\r\n\r\n    public bool IsLoading { get; init; } = false;\r\n\r\n    public int[] Histories { get; init; } = [];\r\n}\r\n\r\npublic class AsyncCounterStore() : Store\u003cAsyncCounterState\u003e(() =\u003e new()) {\r\n    public async Task CountUpAsync() {\r\n        Mutate(state =\u003e state with { IsLoading = true, });\r\n        await Task.Delay(800);\r\n        Mutate(state =\u003e state with {\r\n            IsLoading = false,\r\n            Count = state.Count + 1,\r\n            Histories = [.. state.Histories, state.Count + 1],\r\n        });\r\n    }\r\n}\r\n\r\n```\r\n\r\nFlux Store Pattern\r\n```csharp\r\npublic record AsyncCounterState {\r\n    public int Count { get; init; } = 0;\r\n\r\n    public bool IsLoading { get; init; } = false;\r\n\r\n    public int[] Histories { get; init; } = [];\r\n}\r\n\r\npublic record AsyncCounterCommands : Command {\r\n    public record Increment : AsyncCounterCommands;\r\n    public record BeginLoading : AsyncCounterCommands;\r\n}\r\n\r\npublic class AsyncCounterStore() : FluxStore\u003cAsyncCounterState, AsyncCounterCommands\u003e(() =\u003e new(), Reducer) {\r\n    static AsyncCounterState Reducer(AsyncCounterState state, AsyncCounterCommands command) {\r\n        return command switch {\r\n            AsyncCounterCommands.Increment =\u003e state with {\r\n                Count = state.Count + 1,\r\n                IsLoading = false,\r\n                Histories = [.. state.Histories, state.Count + 1],\r\n            },\r\n            AsyncCounterCommands.BeginLoading =\u003e state with {\r\n                IsLoading = true,\r\n            },\r\n            _ =\u003e throw new CommandNotHandledException\u003cAsyncCounterCommands\u003e(command),\r\n        };\r\n    }\r\n\r\n    public async Task CountUpAsync() {\r\n        Dispatch(new AsyncCounterCommands.BeginLoading());\r\n        await Task.Delay(800);\r\n        Dispatch(new AsyncCounterCommands.Increment());\r\n    }\r\n}\r\n\r\n```\r\n\r\nBlazor view in Razor\r\n```razor\r\n@page \"/counter\"\r\n@inherits ObserverComponet\r\n@inject AsyncCounterStore AsyncCounterStore\r\n\r\n\u003cPageTitle\u003eCounter\u003c/PageTitle\u003e\r\n\u003ch1\u003eAsync Counter\u003c/h1\u003e\r\n\u003cp role=\"status\"\u003eCurrent count: @AsyncCounterStore.State.Count\u003c/p\u003e\r\n\u003cp role=\"status\"\u003eLoading: @AsyncCounterStore.State.IsLoading\u003c/p\u003e\r\n\u003cp role=\"status\" class=\"mb-0\"\u003eHistory\u003c/p\u003e\r\n\u003cdiv class=\"d-flex\"\u003e\r\n    [\r\n    @foreach (var item in string.Join(\", \", AsyncCounterStore.State.Histories)) {\r\n        @item\r\n    }\r\n    ]\r\n\u003c/div\u003e\r\n\u003cbutton class=\"mt-3 btn btn-primary\" @onclick=\"IncrementCount\"\u003eCount up\u003c/button\u003e\r\n\r\n@code {\r\n    async Task IncrementCount() {\r\n        await AsyncCounterStore.CountUpAsync();\r\n    }\r\n}\r\n\r\n```\r\n\r\n# License\r\n\r\nDesigned with ♥ by le-nn. Licensed under the MIT License.\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fle-nn%2Fmemento","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fle-nn%2Fmemento","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fle-nn%2Fmemento/lists"}