Elvenware

JavaScriptReact

Welcome to JavaScriptReact

React

Information on React.

Create React Application

Here is a simple way to create a React Application.

We want create-react-app to open Chrome or Chromium, not FireFox. (Unless you really prefer FireFox. But I tend to use Chrome or Chromium.)

Click the Gear icon (Menu) at the top right of Lubuntu. Choose System Settings. Open up Details. Select Default Applications. Pick Chrome or Chromium, as you prefer. More details are here

npm install -g create-react-app
mkdir ~/Source
cd ~/Source
create-react-app test
cd test
npm start

State

React components track the features that they can change with state. Each component has a state property and when it changes render is called. We change state with this.setState. We usually declare state in a constructor. We display state in the render method.

Each component can have any number of properties, but two properties in React are special:

State tracks the parts of the component that can change.

props are inherited from a component further up the component hierarchy and they can not, or at least should not, be changed.

Rest with fetch

Use fetch to retrieve data from the server. We make REST calls with fetch. For those familiar with jQuery, we will use fetch in lieu of $.ajax or $.getJSON.

fetch will eventually become part of JavaScript in ES6, but for now we have to install it separately:

npm install --save whatwg-fetch

In some cases, you may need to add it to webpack.config.js or config/webpack.config.dev.js in the entry property:

entry: [
    'whatwg-fetch',
    ... and so on
]

Here is a simple call to fetch using ES6 arrow function syntax:

bar = () => {
    const that = this;
    fetch('/api/foo')
        .then(function(response) {
            return response.json();
        }).then(function(json) {
            console.log('json sent from server', json);            
            that.setState(foo => (json));
        }).catch(function(ex) {
            console.log('parsing failed', ex);
        });
};

If the call succeeds, then it is the second .this method that gets the data from the server:

console.log('json sent from server', json);
that.setState(foo => (json));

Here we use it to set a react components state, but exactly what you do with it is up to you. Certainly you don't have to use it set a component's state.

More on fetch

You might notice that there are two calls to .then methods in our fetch promise:

This example checks to reponse.ok in the first .then method of our promise to see that everything is good:

const getServer = (url, dispatch) => {
    fetch(url)
        .then(function(response) {
            if (response.ok) {
                return response.json();
            } else {
                throw new Error(response.statusText + '\n' +
                    response.url + '\n' +
                    'status: ' + response.status);
            }
        }).then(function(json) {
            dispatch({
                type: 'YOU-RANG',
                youRang: json
            });
        }).catch(function(ex) {
            alert(ex);
            console.log('parsing failed', ex);
        });

};

Or in excruciating detail, here are the things you can check when documenting the status of a call:

const getServer = (url, dispatch) => {
    fetch(url)
        .then(function(response) {
            console.log('fetch ok:', response.ok);
            console.log('fetch status:', response.status);
            console.log('fetch statusText:', response.statusText);
            console.log('fetch type:', response.type);
            console.log('fetch url:', response.url);
            console.log('fetch body used:', response.bodyUsed);
            const json = response.json();
            console.log('fetch json:', json);
            if (response.ok) {
                return json;
            } else {
                throw new Error(response.statusText + '\n' +
                    response.url + '\n' +
                    'status: ' + response.status);
            }
        }).then(function(json) {
            dispatch({
                type: 'YOU-RANG',
                youRang: json
            });
        }).catch(function(ex) {
            alert(ex);
            console.log('parsing failed', ex);
        });

};

Mocking Fetch

The next few sections describe how to mock fetch:

Create Mocks Folder

A powerful mock library called Manual Mocks is built into Jest. To use it, start by:

Mock Data

First lets create a simple module that contains the data we will use in our mock and call it __mocks__/mock-data.js:

/**
 * Created by charlie on 4/18/17.
 */

const getData = (url) => {
    switch (url) {
        case './address-list.json':
            return [{
                "firstName": "Lamar",
                "lastName": "Alexander",
                "street": "455 Dirksen Senate Office Building",
                "city": "Washington DC",
                "state": "TN",
                "zip": " 20510",
                "phone": "202-224-4944",
                "website": "https://www.alexander.senate.gov/public",
                "email": "",
                "contact": "http://www.alexander.senate.gov/public/index.cfm?p=Email"
            },
            {
                "firstName": "Roger",
                "lastName": "Wicker",
                "street": "555 Dirksen Senate Office Building",
                "city": "Washington DC",
                "state": "MS",
                "zip": " 20510",
                "phone": "202-224-6253",
                "website": "https://www.wicker.senate.gov",
                "email": "",
                "contact": "https://www.wicker.senate.gov/public/index.cfm/contact"
            },
            {
                "firstName": "Timothy",
                "lastName": "Kaine",
                "street": "231 Russell Senate Office Building",
                "city": "Washington DC",
                "state": "VA",
                "zip": " 20510",
                "phone": "202-224-4024",
                "website": "https://www.kaine.senate.gov",
                "email": "",
                "contact": "https://www.kaine.senate.gov/contact"
            }];

        default:
            return [];
    }
};

export default getData;

This code simply creates sets of data that mimic what our server would return given a call to a specific url.

Mock fetch

Below is the source code for our new mock for fetch called whatwg-fetch.js. Save it and mock-data.js in the __mocks__ folder. Note in particular the call to jest.genMockFromModule. That call asks Jest to generate a mock object for the module we want to replace with a mock:

/**
 * Created by charlie on 4/17/17.
 */

import getData from './mock-data';

'use strict';

const whatwgFetch = jest.genMockFromModule('whatwg-fetch');

const fetch = function(url) {

    const objectState = getData(url);

    const response = {};
    response.json = function() {
        return objectState;
    };

    //console.log("FETCH STATER", objectState);
    return {
        then: function(func) {
            //console.log('FETCH TEST ONE', func(response));
            return {
                then: function(func) {
                    //func(JSON.stringify(stater));
                    func(objectState);
                    return {
                        catch: function() {

                        }
                    };
                }
            };
        }
    };
};

whatwgFetch.fetch = fetch;
window.fetch = fetch;

module.exports = whatwgFetch;

Over time, you can comment out the calls to console.log. But they might be helpful at first when you are trying to understand what is going on. Note in particular that we are now putting calls to the callbacks (func) passed into our labyrinthine series of return statements. The most important is the second call to then where we pass back the stater object. Recall that this is used as follows in our call to fetch:

.then(function (json) {
    console.log('GETONE-FETCH-TWO');
    console.log('parsed json', json);
    that.setState(foo => (json));
})

ENOSPC Error

While testing, If you get an ENOSPC error, do this:

More watches

Props Single Node Error

Here is the error: Method “props” is only meant to be run on a single node. 0 found instead.

I got this when I had a mismatch between what I thought a button click method was called and what it was really called. For instance, here is my test:

wrapper.find('button#foo').simulate('click');

And here is my JSX:

<button id="bar" onClick={this.getBar}>Get User</button>

Note that the ID of the button is bar, but I'm trying to find something with an ID of foo. To fix the proplem, bring them into sync:

wrapper.find('button#bar').simulate('click');

React in WebStorm

NOTE: Assuming you are working with ES6, JSX and React, from the menu select File | Settings | Languages and Frameworks | JavaScript and set the language version to React JSX

Enzyme mount vs shallow

Suppose one component nests another. For instance, suppose that your custom component MyComponent renders a second component called MyOtherComponent. Here is MyComponent's render method :

class MyComponent extends Component {
  render() {
      return (
          <div>
              <MyOtherComponent />
          </div>
      );
  }
}

Here MyComponent does nothing but ask MyOtherComponent to render itself.

In cases like this, if you use Enzyme's shallow, you will only see the output from MyComponent. During the test, it will seem as though MyOtherComponent does not exist, or at least does not render anthing. Indeed, that is often what you want. But sometimes, it is best to see the output from both MyComponent and MyOtherComponent. To do that, you use mount rather than shallow. We usually do this:

import { shallow } from 'enzyme';

If you want to see output from both components, then do this:

import { mount } from 'enzyme';

React Links

Enzym Debug Class

We have several debug functions that we often append to the top of our test files. This violates our DRY principle. As a result, I've created the following simple class which we can reuse in multiple tests:

We could also, at least in theory, add to this class over time as we discover more useful tests.

Promises

Chained Promises