Uncategorized

Elasticsearch

So most people would have probably heard of Elasticsearch by now. So what exactly is Elasticsearch?

Elasticsearch is a distributed, open source search and analytics engine for all types of data, including textual, numerical, geospatial, structured, and unstructured. Elasticsearch is built on Apache Lucene and was first released in 2010 by Elasticsearch N.V. (now known as Elastic). Known for its simple REST APIs, distributed nature, speed, and scalability, Elasticsearch is the central component of the Elastic Stack, a set of open source tools for data ingestion, enrichment, storage, analysis, and visualization. Commonly referred to as the ELK Stack (after Elasticsearch, Logstash, and Kibana), the Elastic Stack now includes a rich collection of lightweight shipping agents known as Beats for sending data to Elasticsearch.

https://www.elastic.co/what-is/elasticsearch

Essentially it is a great tool for analysing data that is stored within indexes inside of a NoSQL type database that is clustered/sharded and fault tolerant. As the blurb above states it is built on top of Lucene. For those that are interested in that, I wrote a small article in the past on using Luecene.Net : https://www.codeproject.com/Articles/609980/Small-Lucene-NET-Demo-App

 

Anyway this post will talk you through downloading Elasticsearch for windows, and will show you how to use the high level C# client called NEST.

 

We will be learning how to do the following things:

  • Create and index new documents
  • Search for documents
  • Update documents
  • Delete documents

So let’s carry on and learn how we can download Elasticsearch.

Download

You can download it from here : https://www.elastic.co/downloads/elasticsearch. For my setup (windows) once downloaded we can simply open the bin folder from the download, and use the BAT file shown in the image below to start it on Windows.

 

image

Once you click that BAT file, and wait a while you should see something like this appear

image

Demo

For this set of demos I am using Visual Studio 2019 (Community), and have installed the following Nuget package for Elasticsearch:

<PackageReference Include="Elasticsearch.Net" Version="7.5.1" />
<PackageReference Include="NEST" Version="7.5.1" />

So with those in place lets proceed to the meat of this post, which is how do we do the things that we said we would do at the start of this post. So lets carry on to look at that. As I say this demo will use the high level Elasticsearch .NET client NEST which you can read more about here : https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/nest.html

Indexing documents

The 1st step is to get some data into Elasticsearch, so to do that we need to craft some data and also Index the data. Elastic is clever enough to infer some of the data/field types that should be used when it indexes but you can override this should you want to. Lets see an example

We will use this class (ONLY) during this demo to do all our operations with

namespace ElasticDemoApp_CSharp.Models
{
    public class Person
    {
        public string Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public bool IsManager { get; set; }
        public DateTime StartedOn { get; set; }
    }
}

It can be seen that there is an Id field in that POCO object. This field is fairly important and we will see why later.Lets see how we can get some data in.

var settings = new ConnectionSettings(new Uri("http://localhost:9200"))
.DefaultIndex("people");

var client = new ElasticClient(settings);

//CREATE
var person = new Person
{
    Id = "1",
    FirstName = "Tom",
    LastName = "Laarman",
    StartedOn = new DateTime(2016, 1, 1)
};

var people = new[]
{
    new Person
    {
        Id = "2",
        FirstName = "Tom",
        LastName = "Pand",
        StartedOn = new DateTime(2017, 1, 1)
    },
    new Person
    {
        Id = "3",
        FirstName = "Tom",
        LastName = "grand",
        StartedOn = new DateTime(2017, 5, 4)
    }
};

client.IndexDocument(person);
client.IndexMany(people);

var manager1 = new Person
{
    Id = "4",
    FirstName = "Tom",
    LastName = "Foo",
    StartedOn = new DateTime(2017, 1, 1)
};

client.Index(manager1, i => i.Index("managerpeople"));

The code above shows you how to create the initial client, and also how to insert a single document, and how to insert many documents. Elastic kind of has a few ways for doing the same thing, so its up to you which API syntax you prefer, but the examples above largely do the same thing, they get data into Elastic at certain indexes. You can read more about Indexing here :https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/indexing-documents.html

Query

So now that we have some data in we may want to Search for it. Elastic comes with a rich query API, which you can read about here : https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/search.html

So here is an example to query the data we just stored in Elastic. Note the use of the “&&” to form complex queries, you can read about that here : https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/bool-queries.html#binary-and-operator. Its worth getting to know these operators as it will make your queries more readable

//SEARCH
var searchResponse = client.Search<Person>(s => s
    .From(0)
    .Size(10)
    .AllIndices()
    .Query(q =>
            q.Match(m => m
            .Field(f => f.FirstName)
            .Query("Tom")
            ) &&
            q.DateRange(r => r
            .Field(f => f.StartedOn)
            .GreaterThanOrEquals(new DateTime(2017, 1, 1))
            .LessThan(new DateTime(2018, 1, 1))
            )
    )
);

var matches = searchResponse.Documents;

Update

So now that we have some data and we can search it, lets turn our hand to updating it. Here are a few examples where I mix in some queries to check the updated data

//UPDATE 

//update all "Tom" person in "people" index
person.FirstName = "Tim";
client.UpdateAsync(new DocumentPath<Person>(person.Id),
    u => u.Index("people")
    .DocAsUpsert(true)
    .Doc(person)
    .Refresh(Elasticsearch.Net.Refresh.True))
    .ConfigureAwait(false).GetAwaiter().GetResult();

searchResponse = client.Search<Person>(s => s
    .From(0)
    .Size(10)
    .AllIndices()
    .Query(q =>
            q.Match(m => m
            .Field(f => f.FirstName)
            .Query("Tim")
            )
    )
);

matches = searchResponse.Documents;

//update "Tim" to "Samantha" using different update method
client.UpdateAsync<Person, object>(new DocumentPath<Person>(1),
    u => u.Index("people")
        .DocAsUpsert(true)
        .RetryOnConflict(3)
        .Doc(new { FirstName = "Samantha" })
        .Refresh(Elasticsearch.Net.Refresh.True))
        .ConfigureAwait(false).GetAwaiter().GetResult();


searchResponse = client.Search<Person>(s => s
    .From(0)
    .Size(10)
    .AllIndices()
    .Query(q =>
        q.Match(m => m
            .Field(f => f.FirstName)
            .Query("Samantha")
        )
    )
);

matches = searchResponse.Documents;

There is not much more to say there apart from perhaps pay special attention to how we use the fluent DSL Doc(…) to apply partial updates, and we also use Refresh(..) which ensures the shards are updated that hold this data, which makes it visible to new searches.

Deleting data

So now we have put data in, queried it, and updated it, guess we should talk about deletes. This is done as follows:

//DELETE
client.DeleteAsync<Person>(1,
    d => d.Index("people")
        .Refresh(Elasticsearch.Net.Refresh.True))
        .ConfigureAwait(false).GetAwaiter().GetResult();

searchResponse = client.Search<Person>(s => s
    .From(0)
    .Size(10)
    .AllIndices()
    .Query(q =>
        q.Match(m => m
            .Field(f => f.Id)
            .Query("1")
        )
    )
);


matches = searchResponse.Documents;

//delete using a query
client.DeleteByQueryAsync<Person>(
    d => d.AllIndices()
        .Query(qry => qry.Term(p => p.Name("FirstName").Value("Tom")))
        .Refresh(true)
        .WaitForCompletion())
        .ConfigureAwait(false).GetAwaiter().GetResult();

var response = client.DeleteByQueryAsync<Person>(
    q => q
        .AllIndices()
        .Query(rq => rq
            .Match(m => m
            .Field(f => f.FirstName)
            .Query("Tom")))
        .Refresh(true)
        .WaitForCompletion())
        .ConfigureAwait(false).GetAwaiter().GetResult();

searchResponse = client.Search<Person>(s => s
.From(0)
.Size(10)
.AllIndices()
.Query(q =>
        q.Match(m => m
        .Field(f => f.FirstName)
        .Query("Tom")
        )
    )
);


matches = searchResponse.Documents;

As before I have included queries in here to check the deletes. Hopefully you get the idea, where below we can delete by an Id, or by using a query where we look to match N-many records.

Demo Project

Anyway that is all I wanted to show this time, hopefully it gives you a small taste of using the .NET Elastic client. You can download a demo project from here : https://github.com/sachabarber/Elasticdemo

React

React router

I plan on getting a lot better at a few things this year, my current list is

  • Really getting to know React
  • Really getting to know AWS
  • Really getting to know Azure

 

As such you can expect some post on all these subjects over a while. But this is the here and now. So in this post I wanted to start with looking at React router

 

What is react router

React Router is a set of React components that help with all your navigation concerns when it comes to working with React.

 

Main Components

It comes with the following main components

 

BrowserRouter

A <Router> that uses the HTML5 history API (pushState, replaceState and the popstate event) to keep your UI in sync with the URL

 

HashRouter

A <Router> that uses the hash portion of the URL (i.e. window.location.hash) to keep your UI in sync with the URL.

 

Link

Provides declarative, accessible navigation around your application.

 

NavLink

A special version of the <Link> that will add styling attributes to the rendered element when it matches the current URL.

 

MemoryRouter

A <Router> that keeps the history of your “URL” in memory (does not read or write to the address bar). Useful in tests and non-browser environments like React Native.

 

Redirect

Rendering a <Redirect> will navigate to a new location. The new location will override the current location in the history stack, like server-side redirects (HTTP 3xx) do.

 

Route

The Route component is perhaps the most important component in React Router to understand and learn to use well. Its most basic responsibility is to render some UI when its path matches the current URL.

 

Router

The common low-level interface for all router components. Typically apps will use one of the high-level routers instead:

The most common use-case for using the low-level <Router> is to synchronize a custom history with a state management lib like Redux or Mobx. Note that this is not required to use state management libs alongside React Router, it’s only for deep integration.

 

StaticRouter

A <Router> that never changes location.

This can be useful in server-side rendering scenarios when the user isn’t actually clicking around, so the location never actually changes. Hence, the name: static. It’s also useful in simple tests when you just need to plug in a location and make assertions on the render output.

 

Switch

Renders the first child <Route> or <Redirect> that matches the location.

 

How is this different than just using a bunch of <Route>s?

<Switch> is unique in that it renders a route exclusively. In contrast, every <Route> that matches the location renders inclusively.

 

Hooks

The current version of the React Router also comes with these hooks

useHistory

The useHistory hook gives you access to the history instance that you may use to navigate.

import { useHistory } from "react-router-dom";

function HomeButton() {
  let history = useHistory();

  function handleClick() {
    history.push("/home");
  }

  return (
    <button type="button" onClick={handleClick}>
      Go home
    </button>
  );
}

useLocation

The useLocation hook returns the location object that represents the current URL. You can think about it like a useState that returns a new location whenever the URL changes.

import React from "react";
import ReactDOM from "react-dom";
import {
  BrowserRouter as Router,
  Switch,
  useLocation
} from "react-router-dom";

function usePageViews() {
  let location = useLocation();
}

useParams

useParams returns an object of key/value pairs of URL parameters. Use it to access match.params of the current <Route>.

import React from "react";
import ReactDOM from "react-dom";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  useParams
} from "react-router-dom";

function BlogPost() {
  let { slug } = useParams();
  return <div>Now showing post {slug}</div>;
}

useRouteMatch

The useRouteMatch hook attempts to match the current URL in the same way that a <Route> would. It’s mostly useful for getting access to the match data without actually rendering a <Route>

import { useRouteMatch } from "react-router-dom";

function BlogPost() {
  let match = useRouteMatch("/blog/:slug");

  // Do whatever you want with the match...
  return <div />;
}

 

So that is the main components and hooks that are available, so lets proceed and see how we can use it

 

How do we use it?

This largely boils down to a few steps.

 

We need an actual React App

Firstly we need an actual app. There are many ways you could do this, but by far the easiest way is to use Create React App. I prefer to work with TypeScript where possible, so we can use something like this : https://create-react-app.dev/docs/adding-typescript/ which will create a simple skeleton react app that will allow the use of TypeScript

 

Lets creates some routes

Once we have created an app, we need to create some actual routes and components to render for the routes. I think the best way to do this is to show a full sample here, then discuss the various different parts as we go. So here is a full example of how to use the React-Router (it should be noted that you will have needed to have installed this via NPM as react-router-dom)

import React, {Component } from "react";
import { RouteComponentProps, useHistory } from 'react-router';
import {
    BrowserRouter as Router,
    Switch,
    Route,
    Link,
    useParams,
    HashRouter,
    BrowserRouter,
    NavLink
} from "react-router-dom";


//Non boostrap version
export default function AppRouter() {

    return (
        <BrowserRouter >
            <div>
                <nav>
                    <ul>
                        <li>
                            <NavLink to={{ pathname: "/" }} activeStyle={{
                                fontWeight: "bold",
                                color: "red"
                            }}>Home</NavLink>
                        </li>
                        <li>
                            <NavLink to="/about" activeStyle={{
                                fontWeight: "bold",
                                color: "red"
                            }}>About</NavLink>
                        </li>
                        <li>
                            <NavLink to="/aboutComponentUsingFunction" activeStyle={{
                                fontWeight: "bold",
                                color: "red"
                            }}>AboutComponentUsingFunction</NavLink>
                        </li>
                        <li>
                            <NavLink to="/aboutComponentUsingRenderFunction" activeStyle={{
                                fontWeight: "bold",
                                color: "red"
                            }}>AboutComponentUsingRenderFunction</NavLink>
                        </li>
                        <li>
                            <NavLink to="/users/1" activeStyle={{
                                fontWeight: "bold",
                                color: "red"
                            }}>Users1</NavLink>
                        </li>
                        <li>
                            <NavLink to="/users/2" activeStyle={{
                                fontWeight: "bold",
                                color: "red"
                            }}>Users2</NavLink>
                        </li>
                        <li>
                            <NavLink to="/users2/1" activeStyle={{
                                fontWeight: "bold",
                                color: "red"
                            }}>Users As Class With History link</NavLink>
                        </li>
                    </ul>
                </nav>

                {/* A <Switch> looks through its children <Route>s and
                    renders the first one that matches the current URL. */}
                <Switch>
                    <Route path="/about">
                        <About />
                    </Route>
                    <Route path="/aboutComponentUsingFunction"
                        //This is bad though due to this statement from the docs
                        //When you use the component props, the router uses React.createElement 
                        //to create a new React element from the given component. 
                        //That means if you provide an inline function to the component attribute, 
                        //you would create a new component every render. 
                        //This results in the existing component unmounting and the new component 
                        //mounting instead of just updating the existing component

                        component={(props: any) => <About {...props} isAuthed={true} />}>
                    </Route>
                    <Route path="/aboutComponentUsingRenderFunction"
                        //This is better as are using render rather than component, which does not 
                        //suffer from the issue mentioned above
                        render={(props: any) => <About {...props} isAuthed={true} />}>
                    </Route>
                    <Route path="/users/:id" children={<Users />} />
                    <Route path="/users2/:id" component={Users2} />
                    <Route path="/">
                        <Home />
                    </Route>
                </Switch>
            </div>
        </BrowserRouter >
    );
}


function Home() {
    return <h2>Home</h2>;
}

function About(props: any) {

    console.log(`In render method of About`);
    console.log(props);
    return <h2>About</h2>;
}

function Users() {
    // We can use the `useParams` hook here to access
    // the dynamic pieces of the URL.
    let { id } = useParams();
    let history = useHistory();

    const handleClick = () => {
        history.push("/home");
    };

    return (
        <div>
            <h3>ID: {id}</h3>
            <button type="button" onClick={handleClick}>Go home</button>
        </div>
    );
}

class Users2 extends React.Component<RouteComponentProps, any> {

    constructor(props: any) {
        super(props);
    }

    render() {
        return (
            <div>
                <h1>Hello {(this.props.match.params as any).id}!</h1 >
                <button
                    type='button'
                    onClick={() => { this.props.history.push('/users/1') }} >
                    Go to users/1
                </button>
            </div>
        );
    }
}

When run this should look like this

image 

 

So from the above code there are a couple of points that deserve special callouts, so lets go through them

 

NavLink usage

We make use of NavLink to declared our actual routes. This would include the to which would be the actual route we wish to be rendered. Some examples would be

  • /
  • /about
  • /aboutComponentUsingFunction
  • /users/1

 

Here is one such example of this

<NavLink to="/about" activeStyle={{
    fontWeight: "bold",
    color: "red"
}}>About</NavLink>

Switch usage

The next thing we need to make sure React-Router is working correctly is to include a Switch block, where we would declare all the routes. A Route should have as a minimum a path and some way of actual rendering the component, such as render/component/children each of which works slightly differently.

 

The path is where you would be able to pick up the parameters for the matched route.  An example for one of the routes that expects some route parameters may look like this

<Route path="/users/:id" children={<Users />} />
<Route path="/users2/:id" component={Users2} />

function Users() {
    // We can use the `useParams` hook here to access
    // the dynamic pieces of the URL.
    let { id } = useParams();
    let history = useHistory();

    const handleClick = () => {
        history.push("/home");
    };

    return (
        <div>
            <h3>ID: {id}</h3>
            <button type="button" onClick={handleClick}>Go home</button>
        </div>
    );
}


class Users2 extends React.Component<RouteComponentProps, any> {

    constructor(props: any) {
        super(props);
    }

    render() {
        return (
            <div>
                <h1>Hello {(this.props.match.params as any).id}!</h1 >
                <button
                    type='button'
                    onClick={() => { this.props.history.push('/users/1') }} >
                    Go to users/1
                </button>
            </div>
        );
    }
}

It can be seen that when we use react-router that the router provides a prop called “match” which we can either expect as props when using class based components or which we may extract using the useParams hook when using a functional React component.

 

It can also be seen from these 2 examples above how we can navigate  to different routes from within our components. This is done using the history object which you can read more about here : https://reacttraining.com/react-router/web/api/history

 

A match object contains information about how a <Route path> matched the URL. match objects contain the following properties:

  • params – (object) Key/value pairs parsed from the URL corresponding to the dynamic segments of the path
  • isExact – (boolean) true if the entire URL was matched (no trailing characters)
  • path – (string) The path pattern used to match. Useful for building nested <Route>s
  • url – (string) The matched portion of the URL. Useful for building nested <Link>s

 

In the example that is provided here I have tried to use to a mixture of render/component/children each of which works differently. Lets go through how these things work

 

render

This allows for convenient inline rendering and wrapping without the undesired remounting explained above.

Instead of having a new React element created for you using the component prop, you can pass in a function to be called when the location matches. The render prop function has access to all the same route props (match, location and history) as the component render prop.

component

A React component to render only when the location matches. It will be rendered with route props.

When you use component (instead of render or children) the router uses React.createElement to create a new React element from the given component. That means if you provide an inline function to the component prop, you would create a new component every render. This results in the existing component unmounting and the new component mounting instead of just updating the existing component. When using an inline function for inline rendering, use the render or the children prop.

children

Sometimes you need to render whether the path matches the location or not. In these cases, you can use the function children prop. It works exactly like render except that it gets called whether there is a match or not.The children render prop receives all the same route props as the component and render methods, except when a route fails to match the URL, then match is null.

 

What about passing extra props to our component?

While the methods described above will give you access to the standard react-router props, what do we do if we want extra props passed to our component? Is this possible? Well yes it is. Lets see one example of this. One such route is this one, where we use the standard react-router props but also supply a further “isAuthed” prop to the component being rendered for the route

 

<Route path="/aboutComponentUsingRenderFunction"
    render={(props: any) => <About {...props} isAuthed={true} />}>
</Route>

function About(props: any) {

    console.log(`In render method of About`);
    console.log(props);
    return <h2>About</h2>;
}

Which when clicked on in the browser will look like this in the console

image

 

 

Lets mount our route based navigation component

We then just need to import this top level react component AppRouter and use it to mount to the DOM, which can be done as follows:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import AppRouter from './AppRouter'


ReactDOM.render(<AppRouter />, document.getElementById('root'));

 

What about bootstrap Navigation?

There are probably quite a lot of people out there that like to use Bootstrap to aid in their website design. There is a React version of Bootstrap called react-bootstrap which has been built specifically for React, so we can use this, it as simple as installing it via NPM as npm install react-bootstrap

 

With that installed, we can also craft a more traditional Bootstrap based navigation component using something like this full example. Note how we use the react-bootstrap NavBar and other components to do the actual navigation now, rather than just nav and list elements like we did in the 1st full example above

import React, { Component } from "react";
import { RouteComponentProps, useHistory } from 'react-router';
import {
    Switch,
    Route,
    useParams,
    BrowserRouter,
    Link
} from "react-router-dom";


import 'bootstrap/dist/css/bootstrap.min.css';
import {
    Nav,
    Navbar
} from "react-bootstrap";


function Navigation() {
    return (
        <BrowserRouter >
            <div>
                <Navbar bg="light" expand="lg">
                    <Navbar.Brand href="#home">React-Bootstrap</Navbar.Brand>
                    <Navbar.Toggle aria-controls="basic-navbar-nav" />
                    <Navbar.Collapse id="basic-navbar-nav">
                        <Nav className="mr-auto">
                            <Nav.Link as={Link} to="/">Home</Nav.Link>
                            <Nav.Link as={Link} to="/about">About</Nav.Link>
                            <Nav.Link as={Link} to="/aboutComponentUsingFunction">AboutComponentUsingFunction</Nav.Link>
                            <Nav.Link as={Link} to="/aboutComponentUsingRenderFunction">AboutComponentUsingRenderFunction</Nav.Link>
                            <Nav.Link as={Link} to="/users/1">Users1</Nav.Link>
                            <Nav.Link as={Link} to="/users/2">Users2</Nav.Link>
                            <Nav.Link as={Link} to="/users2/1">Users As Class With History link</Nav.Link>

                        </Nav>
                    </Navbar.Collapse>
                </Navbar>

                {/* A <Switch> looks through its children <Route>s and
                    renders the first one that matches the current URL. */}
                <Switch>
                    <Route path="/about">
                        <About />
                    </Route>
                    <Route path="/users/:id" render={() => <Users />}/>
                    <Route path="/users2/:id" component={Users2} />
                    <Route path="/">
                        <Home />
                    </Route>
                </Switch>
            </div>
        </BrowserRouter >
    );
}


class AppRouterBootstrap extends Component {
    render() {
        return (
            <div id="App">
                <Navigation />
            </div>
        );
    }
}

export default AppRouterBootstrap;

function Home() {
    return <h2>Home</h2>;
}

function About() {
    return <h2>About</h2>;
}

function Users() {
    // We can use the `useParams` hook here to access
    // the dynamic pieces of the URL.
    let { id } = useParams();
    let history = useHistory();

    const handleClick = () => {
        history.push("/home");
    };

    return (
        <div>
            <h3>ID: {id}</h3>
            <button type="button" onClick={handleClick}>Go home</button>
        </div>
    );
}


class Users2 extends React.Component<RouteComponentProps, any> {

    render() {
        return (
            <div>
                <h1>Hello {(this.props.match.params as any).id}!</h1 >
                <button
                    type='button'
                    onClick={() => { this.props.history.push('/users/1') }} >
                    Go to users/1
                </button>
            </div>
        );
    }
}

The main thing to note here is that we make use of the react-bootstrap components such as Navbar/Nav and Nav.Link.

 

One particular callout should be how we use Nav.Link, where it can be seen that we use like this

import {
    Switch,
    Route,
    useParams,
    BrowserRouter,
    Link
} from "react-router-dom";


import 'bootstrap/dist/css/bootstrap.min.css';
import {
    Nav,
    Navbar
} from "react-bootstrap";

<Nav.Link as={Link} to="/">Home</Nav.Link>

 

See how we use the “as={Link}” we do this to ensure that a full server round trip doesn’t occur. If you use the typical approach of using a href with the react-bootstrap  Nav.Link which is typically as follows

<Nav.Link href="/home">Active</Nav.Link>

 

You would find that a FULL server round trip is done. Which is not what we want, so we instead use the Link component from react-router to use with the react-bootstrap  Nav.Link. This ensures that NO full server round trip is done and the redirect/render occurs purely client side

 

Since the rest of the example is the same as the main example above, I won’t go through it all again.

 

To use this version we would just need to use this instead when we mount the main component to the DOM

ReactDOM.render(<AppRouterBootstrap />, document.getElementById('root'));

 

When run this should look like this

image

 

That’s it for now

Anyways that is all I wanted to say for now. In future react posts, I want to explore React-Redux (which obviously there are many resources for on the internet already, but this is my own journey, so perhaps I will have something useful to say who knows) and how you can properly test React-Redux apps