Skip to content

Commit

Permalink
more docs in the readme
Browse files Browse the repository at this point in the history
  • Loading branch information
Petar Shomov committed Nov 27, 2015
1 parent bb70021 commit ff2325f
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 2 deletions.
94 changes: 92 additions & 2 deletions README.md
Expand Up @@ -3,6 +3,7 @@
[![Build Status](https://img.shields.io/travis/pshomov/reducto.svg?style=flat-square)](https://travis-ci.org/pshomov/reducto)
[![NuGet Release](https://img.shields.io/nuget/v/Reducto.svg?style=flat-square)](https://www.nuget.org/packages/Reducto/)

## What is Reducto?

Reducto is a keeper of the state for your app. It helps to organize the logic that changes that state. Really useful for GUI apps in combination with(but not limited to) MVVM, MVC, MVP etc.

Expand Down Expand Up @@ -31,13 +32,102 @@ PM> Install-Package Reducto
Dispatching an `action` to the store is **the only way to change its state**.<br>
Dispatching an `async action` cannot change the state but it can dispatch `actions` which in turn can change the state.

## Flow
## How does one use this thing?

Coming soon ...
Here is a short example of Reducto in action. Let's write an app that authenticates an user.

First, let's define the `actions` that we will need:

```c#
// Actions
public struct LoginStarted
{
public string Username;
}

public struct LoginFailed {}

public struct LoginSucceeded
{
public string Token;
}
```
Next is the state of our app

```c#
// State
public enum LoginStatus
{
LoginInProgress, LoggedIn, NotLoggedIn
}

public struct AppState
{
public LoginStatus Status;
public String Username;
public String Token;
}
```

Here is how the `actions` change the state of the app

```c#
var reducer = new SimpleReducer<AppState>()
.When<LoginStarted>((state, action) => {
state.Username = action.Username;
state.Token = "";
state.Status = LoginStatus.LoginInProgress;
return state;
})
.When<LoginSucceeded>((state, action) => {
state.Token = action.Token;
state.Status = LoginStatus.LoggedIn;
return state;
})
.When<LoginFailed>((state, action) => {
state.Status = LoginStatus.NotLoggedIn;
return state;
});

var store = new Store<AppState>(reducer);
```
Now let's take a moment to see what is going on here. We made a `reducer` using a builder and define how each `action` changes the state. This `reducer` is provieded to the `store` so the store can use it whenever an `action` is dispatched to it. Makes sense so far? I hope so ;)

Now let's see what is dispatching `actions` to the `store`. One can do that directly but more often then not it will be done from inside an `async action` like this one
```c#
var loginAsyncAction = store.asyncAction(async(dispatch, getState) => {
dispatch(new LoginStarted{Username = "John Doe"});

// faking authentication of user
await Task.Delay(500);
var authenticated = new Random().Next() % 2 == 0;

if (authenticated) {
dispatch(new LoginSucceeded{Token = "1234"});
} else {
dispatch(new LoginFailed());
}
return authenticated;
});
```
A lot going on here. The `async action` gets a _dispatch_ and a _getState_ delegates. The latter one is not used in our case but the former is used a lot. We dispatch an action to signal the login process has started and then again after it has finished and depending on the outcome of the operation. How do we use this `async action`?
```c#
store.Dispatch(loginAsyncAction);
// or if you need to know the result of the login you can do also
var logged = await store.Dispatch(loginAsyncAction);
```

For more examples and please checkout the links below in the Resources section

## Resources

A couple of links on my blog

- [Better MVVM with Xamarin Forms](http://pshomov.github.io/better-mvvm-with-xamarin-forms/)
- [Compartmentalizing logic](http://pshomov.github.io/compartmentalizing-logic/)

## What about the name?

[It is pure magic ;-)](https://en.wikibooks.org/wiki/Muggles%27_Guide_to_Harry_Potter/Magic/Reducto#Overview)
81 changes: 81 additions & 0 deletions src/Reducto.Tests/ExampleTest.cs
@@ -0,0 +1,81 @@
using System;
using NUnit.Framework;
using System.Threading.Tasks;

namespace Reducto.Tests
{
[TestFixture]
public class ExampleTest
{
// Actions

public struct LoginStarted {
public string Username;
}

public struct LoginFailed {}

public struct LoginSucceeded {
public string Token;
}

// State

public enum LoginStatus {
LoginInProgress, LoggedIn, NotLoggedIn
}

public struct AppState
{
public LoginStatus Status;
public String Username;
public String Token;
}

[Test]
public async void all_in_one_example(){
var reducer = new SimpleReducer<AppState>()
.When<LoginStarted>((status, action) => {
status.Username = action.Username;
status.Token = "";
status.Status = LoginStatus.LoginInProgress;
return status;
})
.When<LoginSucceeded>((status, action) => {
status.Token = action.Token;
status.Status = LoginStatus.LoggedIn;
return status;
})
.When<LoginFailed>((status, action) => {
status.Status = LoginStatus.NotLoggedIn;
return status;
});

var store = new Store<AppState>(reducer);

var loginAsyncAction = store.asyncAction(async(dispatch, getState) => {
dispatch(new LoginStarted{Username = "John Doe"});
// faking authentication of user
await Task.Delay(500);
var authenticated = new Random().Next() % 2 == 0;
if (authenticated) {
dispatch(new LoginSucceeded{Token = "1234"});
} else {
dispatch(new LoginFailed());
}
return authenticated;
});

var logged = await store.Dispatch(loginAsyncAction);

if (logged){
Assert.That(store.GetState().Status, Is.EqualTo(LoginStatus.LoggedIn));
} else {
Assert.That(store.GetState().Status, Is.EqualTo(LoginStatus.NotLoggedIn));
}
}
}
}

1 change: 1 addition & 0 deletions src/Reducto.Tests/Reducto.Tests.csproj
Expand Up @@ -43,6 +43,7 @@
<Compile Include="MiddlewareTests.cs" />
<Compile Include="ReducersTests.cs" />
<Compile Include="StoreTests.cs" />
<Compile Include="ExampleTest.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Reducto\Reducto.csproj">
Expand Down

0 comments on commit ff2325f

Please sign in to comment.