Adding React Router to a React App

react-router is a routing library for React, that allows you to declare which components render based on url paths. It’s similar to ng-route and ui-router in AngularJS.

To get started, install react-router npm module:

npm install react-router --save

Full docs are here.

While working on converting an existing web app to React, I ran into a few issues getting my react-router usage working as I wanted.

Here’s my first attempt adding the Router declarations into an existing Container component that renders NavigationComponent – this Component renders a set of navigation links that are common across each of the pages:

<div>
    <NavigationComponent/>
    <Router history={hashHistory}>
        <Route path="/" component={Home}/>
        <IndexRoute component={Home} />
        <Route path="new" component={AddNew} />
        <Route path="search" component={Search} />
    </Router>
</div>

The first error I ran into was this:

Uncaught Error: <Link>s rendered outside of a router context cannot navigate.

Ok, this makes sense, so I need to move the NavigationComponent that renders my Links within <Router> :

<Router history={hashHistory}>
    <Route path="/" component={NavigationComponent}/>
    <IndexRoute component={Home} />
    <Route path="new" component={AddNew} />
    <Route path="search" component={Search} />
</Router>

Routes at the same level replace each other when rendered. To keep the navigation component rendered with the AddNew and Search as child sections of the nav area, nest the Routes as child elements:

<Router history={hashHistory}>
    <Route path="/" component={NavigationComponent}>
        <IndexRoute component={Home} />
        <Route path="new" component={AddNew} />
        <Route path="search" component={Search} />
    </Route>
</Router>

Now, for the NavigationComponent to render it’s child elements, you need to tell it to render:

{this.props.children}

So now this becomes:

render() {
    return (
        <div>
            <div>
                <nav className="navbar navbar-default" role="navigation">
                    <div className="navbar-header">
                        <Link className="navbar-brand" to="/">AddressBook</Link>
                    </div>

                    <div className="collapse navbar-collapse navbar-ex1-collapse">
                        <ul className="nav navbar-nav">
                            <li className="active"><Link to="search">Search</Link></li>
                            <li><Link to="new">New</Link></li>
                        </ul>
                    </div>
                </nav>
            </div>

            <div>
                {this.props.children}
            </div>
        </div>
    )
}

Here’s a good reference for getting started: https://medium.com/reactspeed/create-basic-navigation-components-using-react-router-475bc55a517f#.itkvn5st9

 

 

Adding Jest to an ES5 project (‘Unexpected token’ errors on JSX)

So far I’ve been using Jest with ES6 (see here), but to run Jest tests against React code using ES5, you might get errors when running your test and rendering the JSX.

Per the Jest docs, install the required Jest dependencies:

npm install --save-dev jest babel-jest babel-preset-es2015 babel-preset-react react-test-renderer

If you run ‘npm test’ at this point, any tests matching the *.test.* pattern will get executed, but you’ll probably also see an error ‘Unexpected token’ whenever a render() is called that contains JSX.

The quick fix in this SO question is to add a .babelrc containing this:

{ “presets”: [“es2015”, “react”] }

and then when Jest runs against your React components Babel will know how to transpile your JSX.

More React unit testing with Jest and Enzyme

I’ve been writing some additional tests to get a better idea of how to use Jest and Enzyme for testing my React components (see my first attempts here).

I have a simple Flux app that had a LabelComponent and a ButtonComponent. The full source is here. I want to test the render output is what I expect. The render method on my LabelComponent is pretty simple:

render() {
    return (
        <div className="label">
            Text: { this.state.labelValue.value }
        </div>
    );
}

This component has state which comes from a Store, so this seems a little more interesting to test.

Using Enzyme you can either fully render a component to a virtual DOM using mount() and then check the results, or you can shallow() render which only renders that component and not any additional child dependencies (this is probably more useful for unit tests).

Using mount(), you can check for expected rendered output in the resulting DOM using .contains() – without doing anything to initialize the Store, this is my first step:

import React from 'react';
import {shallow, mount} from 'enzyme';
import LabelComponent from '../components/LabelComponent';
it('renders with text value', () => {

    const result = mount(<LabelComponent/>);
    expect(result.contains(<div className="label">Text: </div>));
});

This is good so far, but I’m really more interested in how the component renders when the Store contains values to be rendered. I’m not sure if I’ve found the right way to do this, but looking for info on how to mock out other parts of my app, like the Store, took me down a rabbit hole. The Jest docs for mocking are not particularly useful. In the api reference there’s something that looks like it allows you to setup a mock return from a function, but it doesn’t have enough info to tell you exactly how to use it.

It seems the Jest api has changed enough over time that it’s hard to find any articles that are current, and show you how the current api should be used. This SO post has a question that’s pretty close to what I was trying to do. There’s two answers that combined together got me to a working solution.

This approach didn’t work for me:

const getData = jest.fn().mockImplementation(() => {
    return {
        getData: jest.fn().mockReturnValue({'labelValue' : { 'value' : 'test'} } )
    }
});

… but, this approach did exactly what I needed:

jest.mock('../stores/LabelStore');
import LabelStore from '../stores/LabelStore';
const getData = jest.fn().mockImplementation(function() {
    return {'labelValue' : { 'value' : 'testvalue'} }
    }
);
LabelStore.getData = getData.bind(LabelStore);

As far as I work out, this is mocking my getData() function to return the mocked data I want for testing, and then binding it to a mocked LabelStore.

I’m not sure if this is the best approach for mocking with Jest, but this is doing exactly what I need. If I learn more about this approach I’ll come back and provide an update later.

Spring Boot RestController Error: “No converter found for return value of type”

Spring Boot RestControllers by default can return a Pojo class as the return result from a mapped request method, and it is converted into a Json result. I’ve run into this issue before though, and it’s not immediately obvious what’s wrong: ‘No converter found for return value of type’ :

java.lang.IllegalArgumentException: No converter found for return value 
of type: class kh.springboot.redis.domain.RedisResult
at org.springframework.web.servlet.mvc.method.annotation
.AbstractMessageConverterMethodProcessor
.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:187) ~[spring-webmvc-4.3.6.RELEASE.jar:4.3.6.RELEASE]

My returned class from my @GetMapping method is just a simple Pojo:

public class RedisResult {

private String key;
private String value;

public RedisResult(String key, String value) {
  this.key = key;
  this.value = value;
  }
}

And my @RestController is a simple controller with a single @GetMapping (in this case I’m building a REST endpoint to query key values from Redis using Spring Data Redis RedisTemplate:

@RestController

public class RedisRestController {

  @Autowired
  @Qualifier("RedisTemplate")
  private RedisTemplate template;

  @GetMapping("singlekey/{key}")
  public RedisResult getSingleValue(@PathVariable("key") String key){
    String value = (String)this.template.opsForValue().get(key);
    RedisResult result = new RedisResult(key, value);
    return result;
  }
}

If you’ve found my post because you have this same issue, before you go down the rabbit hole adding additional Maven dependencies or additional annotations you think might be missing, the reason is usually that your Pojo class you are returning doesn’t have any public getter methods. For each property in your Pojo you want returned in your Json, make sure you have a public getter.

This is discussed in this StackOverflow post here.