What about the backend?

Remember when we discussed how React.js is a UI library and that your business logic goes elsewhere?

Well the time has come to focus on that business logic.

For this we’ll actually use the same project we’re already in (“MyTweets” in my case) but there’s absolutely no reason the code we’re about to write couldn’t be moved into its own project and then shared between different UI projects.

The requirement

So we need to return a list of tweets when the UI asks for it.

The UI will make a request (“please give me a list of tweets”) and our application will respond accordingly.

This Request/Response pattern is repeated in virtually all web applications and enables you to:

a) Retrieve data (query) e.g. retrieve a list of tweets

b) Tell your application to do something (command) e.g. post a new tweet

1571399838799

We need a way to represent this Request/Response pattern in our application and, if we get this right, we can re-use the same application logic whether we opt for React.js, Blazor, Angular or anything else for our UI.

Retrieve a list of tweets using MediatR

There are several ways we can go about implementing this application logic but my preferred one is to use a small library by Jimmy Bogard, called MediatR.

Start by adding this NuGet package to your application; either in Visual Studio via the Package Manager console:

Install-Package MediatR.Extensions.Microsoft.DependencyInjection

Or via a terminal/command line:

dotnet add package MediatR.Extensions.Microsoft.DependencyInjection

This will install both MediatR and another package for integrating MediatR with the built-in Microsoft Dependency Injection framework.

Now we need a way to represent our Request and Response in code.

Here’s how I’d do it.

Add a new Features Directory to your project.

In there create a new directory called Tweets.

Now create a List.cs class in our new Tweets folder.

1572863934078

This is where we’re going to represent our Request (query) and Response (model). We’ll start by replacing the contents of List.cs with the following:

using MediatR;
using System.Collections.Generic;

namespace MyTweets.Features.Tweets
{
    public class List
    {
        public class Query : IRequest<Model>
        {
        }

        public class Model
        {
            public List<string> Tweets { get; set; }
        }
    }
}

Our Query class has no properties because we’re not yet thinking about things like search (in which case we’d need to include a property for SearchTerm here), rather we’re just saying we want all tweets.

The Model class represents the shape of the data we want to return (in this case, a list of strings which represents our tweets).

Now all we need is a way to handle this request and return our model.

Add a QueryHandler class to the end of your List class (directly below Model).

{
    // existing Model class    
	public class Model
	{
    	public List<string> Tweets { get; set; }
	}

	// add this nested class...
	public class QueryHandler : IRequestHandler<Query, Model>
    {
        public async Task<Model> Handle(Query request, CancellationToken cancellationToken)
        {
            return new Model
            {
                Tweets = new List<string>
                {
                    "One from the server",
                    "Two from the server",
                    "Three from the server",
                    "Four"
                }
            };
        }
    }
}

Make sure you’ve added these using statements and you’re good to go.

using MediatR;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

That’s all we need (for now). A simple hardcoded list of tweets.

Hooking it all up

There’s just a couple more things to do if we’re going to invoke this request from our React frontend.

First we need to tell our application we’re using MediatR (so it can find and wire up all our handlers, like the one we just created above).

Head over to Startup.cs and add this anywhere in ConfigureServices:

services.AddMediatR(Assembly.GetExecutingAssembly());

This instructs MediatR to look for any handlers in the currently executing assembly (or .NET Core API/React project).

You’ll also need to add two using statements to the top of startup.cs:

using MediatR;
using System.Reflection;

Now we have a handy query to run, which will return a list of tweets, but we have no way of running it yet.

We need a way to invoke this query from our React application, take the results and put them in our React component’s state.

For this we can employ ASP.NET Core Web API.

Add a new controller to the Features/Tweets folder and call it anything you like (I called mine TweetController.cs).

Replace the contents with the following:

using System.Threading.Tasks;
using MediatR;
using Microsoft.AspNetCore.Mvc;
using MyTweets.Features.Tweets;

[Route("[controller]")]
[ApiController]
public class TweetController : ControllerBase
{
    private readonly IMediator mediator;

    public TweetController(IMediator mediator)
    {
        this.mediator = mediator;
    }

    [HttpGet]
    public async Task<IActionResult> List()
    {
        var model = await mediator.Send(new List.Query());
        return Ok(model);
    }
}

This is how you can invoke any MediatR request, using the Send method.

In this case we instantiate a new instance of our List.Query class.

When we “send” this via MediatR, it will locate and execute our handler and return the resulting data (our list of “tweets”).

Call your API from React

Now we can return tweets from our API you can actually test this in the browser.

Head over to https://<your-app-here>/tweet and you should see the raw JSON tweets data displayed in your browser.

But remember, we have a shiny new React.js component that we want to use to show these tweets, let’s put that to work.

Head over to the ClientApp/src/components/Tweets/List.js component.

Remember this code?

state = {
    tweets: [
        "One tweet",
        "Two tweets",
        "Three tweets",
        "Four"
    ]
}

We need to replace this hardcoded state with the data from a call to our API.

For this we can use the native HTTP Fetch API built into javascript (and supported by most modern browsers).

Add a componentDidMount method below the state declaration in List.js (and above the ‘Render’ method) as follows:

async componentDidMount() {
    const response = await fetch('tweet');
    const data = await response.json();
    this.setState({tweets: data.tweets});
}

But, run this now and you may well get a nasty looking error!

So what gives?

Handling nulls

Our code in the render method is attempting to loop over the tweets array…

{this.state.tweets.map(tweet => <Tweet text={tweet}/>) }

The error occurs because, until the network fetch returns data, this.state.tweets is not set to anything.

In this case state.tweets is literally null.

So when we try to call map on it, javascript complains that it can’t call map on something which is null.

We know that state.tweets will be set to an array of tweets one the network call finishes, but by that time React has given up waiting and already shouted its loud, angry looking error at us!

We can easily avoid this however if we simply provide some default state for the list of tweets…

export default class List extends React.Component {

    state = { tweets:[] }
    
    async componentDidMount() {
        const response = await fetch('tweet');
        const data = await response.json();
        this.setState({tweets: data.tweets});
    }
    
    render() {
        return (
            <>
                <h3>Tweets</h3>
                {this.state.tweets.map(tweet => <Tweet text={tweet}/>) }
            </>);
    }
}

Now React will happily render our component (initially with an empty list of tweets), then update the UI once the network call completes.

Give it a spin

Run your application now; your React component will make a network call to the API, retrieve a list of tweets and render them in the browser!

1571401155116

Nice!

Making progress

It’s worth taking a moment to reflect on what we’ve achieved up to this point.

We now have a clean architecture which enables us to build our application’s logic (queries and commands) in a way which doesn’t box us in to any specific UI framework.

Let’s say we suddenly needed to rewrite our UI (or a part of it) using Blazor Server.

No problem; our MediatR request, response and handlers all stay the same, we would would just invoke them from our Blazor Server components directly (instead of going via an API controller).

Pretty neat huh?

Next we’ll look at introducing a lightweight data store for testing so we can make our application handle new tweets.