I command you…

If the last lesson was all about representing queries in our application (“please give me this data”) this one is all about issuing commands.

Commands generally change data in your application and execute key business logic.

The obvious command for us to build first is one for posting new tweets.

But, hold your horses, you may have noticed we don’t actually have a “store” for our tweets yet; the query we added last time just returns hardcoded data.

We could go ahead and bring in an actual database at this point, maybe utilising something like EF Core, but our focus is to figure out how to model commands and queries, and hook them up to a front end (React.js in this case) so we’ll take a pragmatic approach and use a fake “in memory” data store so we can keep moving forward.

To create a fake data store for tweets we really just need to hold a collection of them in memory somewhere. We can then read from, and add to this collection and it will act as a proxy for a real database at some point down the line.

Add this Store.cs class somewhere in your application (a Data folder at the root of your application looks like as good a place as any).

using System.Collections.Generic;
using System.Linq;

namespace MyTweets.Data
{
    public class Store
    {
        public IList<Tweet> Tweets { get; set; } = new List<Tweet>
        {
            new Tweet
            {
                Contents = "One from the backend"
            }
        };

        public void Add(Tweet tweet)
        {
            Tweets.Add(tweet);
        }
    }

    public class Tweet
    {
        public string Contents { get; set; }
    }
}

So this is really just a model (Tweet) and a collection to store Tweets in. This class also exposes a method for adding a new tweet.

If you’ve used Entity Framework Core you’ll be familiar with the DbContext. This Store class is essentially our pretend DBContext and is merely here to give us somewhere to “store” our tweets.

To use this “store” in our application we’ll want to register it with the Microsoft Dependency Injection framework (thus making it available to be injected into any class in our application).

Add the following to ConfigureServices in Startup.cs.

services.AddSingleton<Store>();

(you’ll need to add a using statement: using MyTweets.Data;)

With this in place, our application will hold one instance of this Store and forward all requests to it. This will suffice for testing our application but it’s worth reiterating; you’d want to swap this out for a real database for anything that’s actually going to be used by real people!

Update List.cs to fetch from the new store

We have a store but we’re still retrieving hardcoded tweets at the moment when we run our List query. Let’s switch that over to use the new store.

Replace the contents of QueryHandler in List.cs with this…

public class QueryHandler : IRequestHandler<Query, Model>
{
    private Store _store;

    public QueryHandler(Store store)
    {
        _store = store;
    }

    public async Task<Model> Handle(Query request, CancellationToken cancellationToken)
    {
        return new Model
        {
            Tweets = _store.Tweets
                .Select(x => x.Contents)
                .ToList()
        };
    }
}

We’ve used ASP.NET Core’s dependency injection to bring in our Tweet store and map the Contents of each tweet into our Model (which, if you recall is simply a list of strings).

It’s all in the command

Now we need to create our command to post a new tweet.

Realistically this will take in the tweet contents, then add it to our store. Unlike Queries, Commands don’t necessarily return in a response.

1571603099375

We can model this in a very similar way to our earlier Query…

Add a Post.cs class to Features/Tweets and replace the contents with this…

using MediatR;
using System.Threading;
using System.Threading.Tasks;
using MyTweets.Data;

namespace MyTweets.Features.Tweets
{
    public class Post
    {
        public class Command : IRequest
        {
            public string Text { get; set; }
        }
    }
}

This is our command. All we need (for now) are the contents of the tweet itself.

Now we can add a handler, just below the Command class (but still nested inside the Post class).

public class CommandHandler : AsyncRequestHandler<Post.Command>
{
    private readonly Store store;

    public CommandHandler(Store store)
    {
        this.store = store;
    }

    protected override async Task Handle(Post.Command request, CancellationToken cancellationToken)
    {
        store.Add(new Tweet { Contents = request.Text });
    }
}

Because this is a command and doesn’t need to return data, I’ve opted to implement AsyncRequestHandler instead of IRequestHandler. The main difference being AsyncRequestHandler doesn’t require us to return a response.

Call “Post” from our controller

Now we can add a new action to our TweetController. Just below the existing List method will do.

[HttpPost]
public async Task<IActionResult> Post([FromBody] Post.Command command)
{
    var model = await mediator.Send(command);
    return Ok(model);
}

Note how similar this is to the List method. As far as MediatR is concerned this is no different, we’re simply “sending” an instance of our request.

The main change from our perspective is that we’re letting ASP.NET populate our Post.Command from the incoming request, then handing this over to MediatR (which will locate and execute our handler).

A little bit of UI goes a long way

Now we just need to “hook this up” to the front end.

For that we’ll want some mechanism to type out our new Tweet and “post” it.

Head over to the components/Tweets/ folder and add a new javascript file called PostNew.js.

Replace its contents with the following…

import React from 'react';

export default class PostNew extends React.Component {

    state = {text: ''};
    
    async handleSubmit(event) {
        alert('boo');
        event.preventDefault();
    }

    handleChange(event) {
        this.setState({text: event.target.value});        
    }

    render() {
        return (
            <form onSubmit={this.handleSubmit}>
                <div className="form-group">
                    <textarea className="form-control" value={this.state.text} onChange={this.handleChange}/>
                </div>
                <button type="submit" className="btn btn-primary">
                    Tweet it!
                </button>
            </form>
        )
    }
}

Aside from a little Bootstrap, we’re using standard HTML elements here.

The interesting parts are the two functions; handleSubmit and handleChange.

First handleChange handles the text area’s onChange event. When this fires (because the textarea’s value changes) handleChange kicks in, looks for the new value (from event.target.value) and uses it to update our component’s state with the entered value.

Whenever we type something into the text area, the text value in state will be updated based on what we typed in.

We also need to handle the form submitted event. handleSubmit does this and, for now, simply throws an alert in the browser stating “boo”.

The joy of “this”

Sadly, javascript being javascript, this code will error in the browser.

Give it a spin and you’ll most likely see this error…

1572865707776

So what’s this all about?

Well this in javascript is a little… confusing, and has a tendency to refer to different things at different times.

The problem here is that this has lost its binding, and fallen back to the default which (in this case) is undefined.

Why this happens is a little beyond the scope of this short course, but has to do with the fact that the onChange event is raised from a textarea control, and this is bound to a value based on where the event is raised, not our handling function.

But, fear not, we can wrestle back control of this and make it do what we actually want.

For this we’ll need to add a constructor. Add this code somewhere near the top of your PostNew.js component.

constructor(props){
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
}

When we add a constructor to a React component we have to manually forward props on to the React.Component base class, hence the call to super(props).

Now we manually bind handleChange to this in our constructor. At this point, in our constructor, this refers to our component class itself.

I’ve also done the same for handleSubmit as we’re about to need that too.

this is pretty confusing eh?!

But, the key takeaway is, bind this to your functions (in the constructor) and you’ll find this referring to the class you’re in. Test this in the browser now; everything should render without errors, and if you click the button you should see “boo” in an alert!

Call the backend API

So finally, to make this work, we need to update handleSubmit to actually call our API, including the tweet’s text.

Replace the contents of handleSubmit with this…

async handleSubmit(event) {
    event.preventDefault();
    const options = {
        method: 'POST',
        body: JSON.stringify(this.state),
        headers: { 'Content-Type': 'application/json' }
    };
    await fetch('Tweet', options);        
}

This uses fetch but this time to initiate a POST to our API, which should then grab the value of text and use it to add a new Tweet to our temporary Tweet store.

Time to tweet

So we have a PostNew component but we’re not showing it anywhere (yet).

You can show this component wherever you want but for now I’ll render it in List.js.

Change render() to include an instance of our PostNew component as follows…

render() {
	return (
	<>
		<h3>Tweets</h3>
		<PostNew/>
		{this.state.tweets.map(tweet => <Tweet text={tweet}/>)}
	</>
	);
}

With this in place launch your application in the browser and try posting a new tweet.

1572868846995

The tweet should be added to our store; refresh the page to see it appear in the list of tweets!

We’re nearly there, but that last step (refreshing to see the new tweet) feels a little, well, clunky…

A better option would be to trigger a refresh of the tweets when a new one is posted.

That’s what we’ll cover in the next and final lesson (as well as a quick recap on what we’ve seen plus some bonus tasks for you to have a stab at yourself).