webpack | Matthew Daly's Bloghttps://matthewdaly.co.uk/blog/categories/webpack/
webpack | I'm a web developer in Norfolk. This is my blog...Sat, 10 Mar 2018 15:12:45 GMThttp://blogs.law.harvard.edu/tech/rssgrunt-blogbuilder https://github.com/matthewbdaly/grunt-blogbuilderMatthew Daly 2018https://matthewdaly.co.uk/blog/2016/08/15/creating-a-personal-dashboard-with-react-and-webpack/
https://matthewdaly.co.uk/blog/2016/08/15/creating-a-personal-dashboard-with-react-and-webpack/Mon, 15 Aug 2016 22:18:00 GMTThe Raspberry Pi is a great device for running simple web apps at home on a permanent basis, and you can pick up a small touchscreen for it quite cheaply. This makes it easy to build and host a small personal dashboard that pulls important data from various APIs or RSS feeds and displays it. You’ll often see dashboards like this on Raspberry Pi forums and subreddits. As I’m currently between jobs, and have some time to spare before my new job starts, I decided to start creating my own version of it. It was obvious that React.js is a good fit for this as it allows you to break up your user interface into multiple independent components and keep the functionality close to the UI. It also makes it easy to reuse widgets by passing different parameters through each time.

In this tutorial I’ll show you how to start building a simple personal dashboard using React and Webpack. You can then install Nginx on your Raspberry Pi and host it from there. In the process, you’ll be able to pick up a bit of knowledge about Webpack and ECMAScript 2015 (using Babel). Our initial implementation will have only two widgets, a clock and a feed, but those should show you enough of the basics that you should then be able to build other widgets you may have in mind.

Note the various loaders we’re using. We use ESLint to lint our Javascript files for code quality, and the build will fail if they do not match the required standards. We’re also using loaders for CSS, Sass, Babel (so we can use ES2015 for our Javascript) and fonts. Also, note the hot module replacement plugin - this allows us to reload the application automatically. If you haven’t used Webpack before, this config should be sufficient to get you started, but I recommend reading the documentation.

We also need to configure ESLint how we want. Here is the configuration we will be using, which should be saved as .eslintrc.yml:

rules:

no-debugger:

-0

no-console:

-0

no-unused-vars:

-0

indent:

-2

-2

quotes:

-2

-single

linebreak-style:

-2

-unix

semi:

-2

-always

env:

es6:true

browser:true

node:true

extends:'eslint:recommended'

parserOptions:

sourceType:module

ecmaFeatures:

jsx:true

experimentalObjectRestSpread:true

modules:true

plugins:

-react

We also need a base HTML file. Save this as index.html:

<!doctype html>

<htmllang="en">

<head>

<metacharset="utf-8">

<title>Personal Dashboard</title>

</head>

<body>

<divid="view"></section>

<scriptsrc="bundle.js"></script>

</body>

</html>

We also need to set the commands for building and testing our app in package.json:

The npm test command will call Mocha to run the tests, but will also use Istanbul to generate test coverage. For the sake of brevity, our tests won’t be terribly comprehensive. The npm start command will run a development server, while npm run build will build our application.

We also need to create the test/ folder and the test/setup.js file:

import jsdom from'jsdom';

import chai from'chai';

const doc = jsdom.jsdom('<!doctype html><html><body></body></html>');

const win = doc.defaultView;

global.document = doc;

global.window = win;

Object.keys(window).forEach((key) => {

if (!(key in global)) {

global[key] = window[key];

}

});

This sets up Chai and creates a dummy DOM for our tests. We also need to create the folder js/ and the file js/app.js. You can leave that file empty for now.

Our dashboard component

Our first React component will be a wrapper for all the other ones. Each of the rest of the components will be a self-contained widget that will populate itself without the need for a centralized data store like Redux. I will mention that Redux is a very useful library, and for larger React applications it makes a lot of sense to use it, but here we’re better off having each widget manage its own data internally, rather than have it be passed down from a single data store.

No coverage information was collected, exit without writing coverage information

Our first component is in place. However, it isn’t getting loaded. We also need to start thinking about styling. Create the file scss/style.scss, but leave it blank for now. Then save this in js/app.js:

import React from'react';

import ReactDOM from'react-dom';

import Dashboard from'./components/dashboard';

import styles from'../scss/style.scss';

ReactDOM.render(

<Dashboardtitle="My Dashboard" />,

document.getElementById('view')

);

Note that we’re importing CSS or Sass files in the same way as Javascript files. This is unique to Webpack, and while it takes a bit of getting used to, it has its advantages - if you import only the styles relating to each component, you can be sure there’s no orphaned CSS files. Here, we only have one CSS file anyway, so it’s a non-issue.

If you now run npm start, our dashboard gets loaded and the title is displayed. With our dashboard in place, we can now implement our first widget.

Creating the clock widget

Our first widget will be a simple clock. This demonstrates changing the state of the widget on an interval. First let’s write a test - save this as test/components/clockwidget.js:

Note that the component accepts a property of time. The getInitialState() method then converts this.props.time into this.state.time so that it can be displayed on render. Note we also set a default of the current time using Moment.js.

We also need to update the dashboard component to load this new component:

import React from'react';

import ClockWidget from'./clockwidget';

exportdefault React.createClass({

render() {

return (

<divclassName="dashboard">

<h1ref="title">{this.props.title}</h1>

<divclassName="wrapper">

<ClockWidget />

</div>

</div>

);

}

});

Now, if you try running npm start and viewing the dashboard in the browser, you will see that it displays the current time and date, but it’s not being updated. You can force the page to reload every now and then, but we can do better than that. We can set an interval in which the time will refresh. As the smallest unit we show is seconds, this interval should be 1 second.

When our component has mounted, we set an interval of 1,000 milliseconds, and each time it elapses we call the tick() method. This method sets the state to the current time, and as a result the user interface is automatically re-rendered. On unmount, we clear the interval.

In this case we’re just calling a single function on a set interval. In principle, the same approach can be used to populate components in other ways, such as by making an AJAX request.

Creating an RSS widget

Our next widget will be a simple RSS feed reader. We’ll fetch the content with jQuery and render it using React. We’ll also reload it regularly. First, let’s create our test:

Our feed widget will accept an external URL as an argument, and will then poll this URL regularly to populate the feed. It also allows us to specify the size attribute, which denotes the number of feed items, and the delay attribute, which denotes the number of seconds it should wait before fetching the data again.

Warning: React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components). Check the render method of `dashboard`.

1) renders the dashboard

Feed Widget

Warning: React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components).

This is by far the most complex component, so a little explanation is called for. We include jQuery as a dependency at the top of the file. Then we create a component for rendering an individual feed item, called FeedItem. This is very simple, consisting of an anchor tag wrapped around a list item. Note the use of the const keyword - in ES6 this denotes a constant.

Next, we move onto the feed widget proper. We set the initial state of the feed to be an empty array. Then, we define a componentDidMount() method that calls getFeed() and sets up an interval to call it again, based on the delay property. The getFeed() method fetches the URL in question and sets this.state.feed to an array of the most recent entries in the feed, with the size denoted by the size property passed through. We also clear that interval when the component is about to be umounted.

Note that you may have problems with the Access-Control-Allow-Origin HTTP header. It’s possible to disable this in your web browser, so if you want to run this as a dashboard you’ll probably need to do so. On Chrome there’s a useful plugin that allows you to disable this when needed.

Because our FeedWidget has been created in a generic manner, we can then include multiple feed widgets easily, as in this example:

With that done, feel free to add whatever other feeds you want to include.

Deploying our dashboard

The final step is deploying our dashboard to our Raspberry Pi or other device. Run the following command to generate the Javascript:

$ npm run build

This will create static/bundle.js. You can then copy that file over to your web server with index.html and place both files in the web root. I recommend using Nginx if you’re using a Raspberry Pi as it’s faster and simpler for static content. If you’re likely to make a lot of changes you might want to create a command in the scripts section of your package.json to deploy the files more easily.

These basic widgets should be enough to get you started. You should be able to use the feed widget with virtually any RSS feed, and you should be able to use a similar approach to poll third-party APIs, although you might need to authenticate in some way (if you do, you won’t want to expose your authentication details, so ensure that nobody from outside the network can view your application). I’ll leave it to you to see what kind of interesting widgets you come up with for your own dashboard, but some ideas to get you started include:

Public transport schedules/Traffic issues

Weather reports

Shopping lists/Todo lists, with HTML5 local storage used to persist them