Sunday, 2 June 2019

CurlScan - How to find Curly-Girl Approved Hair Care Prodcuts

Check out my latest side-project: CurlScan

CurlScan is a product lookup and barcode scanner web application that checks if hair care products are suitable for people with curly hair. It's mobile friendly and supports searching by product name or by UPC/EAN number. If you don't know what you are looking for you can read through an alphabetised list of approved products.

Sunday, 18 March 2018

Customize the AJAX request for Ant Design Component (React JS)

If you want to implement your own upload AJAX handler for the React Ant Design Library Upload or Dragger components then the docs may not be much help. The missing details are below:

class UploadFile extends React.Component {

  render() {
    const draggerProps = {
      name: "file",
      multiple: false,
      showUploadList: false,
      customRequest: ({onSuccess, onError, file}) => {
        fetch('/upload', {
          method: 'POST',
          success: (resp) => {
          failure: (err) => {

    return (
        <Upload.Dragger {...draggerProps}>
          <p classname="ant-upload-drag-icon">
            <Icon type="cloud-upload"/>
             Drag & Drop a file           

You just need to implement a customRequest function that accepts an object containing two callback functions: onSuccess, onError and the file to be uploaded. You just need to do whatever custom upload stuff you have to do in this function and then call either onSuccess() or onError() as appropriate when you get your response back. You don't have to pass any parameters to these functions

How to use Ant Design library with Isomorphic style loader in React Starter Kit Project

I wanted to use isomorphic-style-loader in combination with Ant Design in a project built using React Starter Kit so that I could use the less processor to customise the default Ant styles. If you try to do it the way suggested by the Ant website using babel-import-plugin:

module: {
    rules: [
        loader: 'babel-loader,
        options: {
            plugins: [
                ['import', {libraryName: 'antd', style: true}]

Then you'll quickly run into this type of issue:

[19:51:42] Launching server...
(function (exports, require, module, __filename, __dirname) { @import "./themes/default";

SyntaxError: Invalid or unexpected token
    at createScript (vm.js:80:10)
    at Object.runInThisContext (vm.js:139:10)
    at Module._compile (module.js:607:28)
    at Module._extensions..js (module.js:654:10)
    at Object.require.extensions.(anonymous function) [as .js] (/home/frontend/node_modules/babel-register/lib/node.js:152:7)
    at Module.load (module.js:556:32)
    at tryModuleLoad (module.js:499:12)
    at Function.Module._load (module.js:491:3)
    at Module.require (module.js:587:17)
    at require (internal/module.js:11:18)
error An unexpected error occurred: "Command failed.
Exit code: 1

Why it doesn't work

This is because the babel-import-plugin transforms this:

import {Button} from 'antd'


var _button = require('antd/lib/button')

In hindsight, I don't know why/how I ever thought this could work.

The whole point of using the isomorphic style loader is to be explicit about which style files are required by each component so your sever can package only the ones required for the initial page render. This is antithetical to what is happening with the babel-import-plugin output, which is just injecting require statements into files and not keeping track of any association with components. If you were using the regular style-loader this would be sufficient to ensure the styles were injected into the head of the html document. If you are using the isomorphic-style-loader then you are generating  the html dynamically in your server code and using it's API to insert just the styles in the current React provider context, so none of the Ant styles will get included.

Furthermore, if you are using a common module config in your webpack.config.js file for the server and client then by enabling babel-import-plugin you'll end up trying to require the Ant '.less' files from your node app which will always fail because less syntax is not valid javascript and library code (stuff in /node_modules/ ) is excluded from webpack bundles by default!

So what is the answer?

I found the simplest solution is to use the isomorphic-style-loader withStyles function to wrap the top-level application React component with *all* the styles needed by my app. The downside is that you'll ship more styles than are required for the initial render but probably not that much more, since if you are using Ant in your project, you're probably using it extensively throughout the app. The upside is that the code is explicit. I created a JS module called withAntStyles.js:

import React from 'react'
import withStyles from 'isomorphic-style-loader/lib/withStyles'

// Styles includes
// Include just the styles you are using in your project. 
// You'll have to remember to add new ones as you go along
import antdStyles from 'antd/lib/style/index.less'
import layoutStyles from 'antd/lib/layout/style/index.less'
import gridStyles from 'antd/lib/grid/style/index.less'
import badgeStyles from 'antd/lib/badge/style/index.less'
import uploadStyles from 'antd/lib/upload/style/index.less'

function withAntStyles(AntStyledComponent) {
  return withStyles(

export default withAntStyles

So now all you have to do is wrap your top most application React Component, e.g.:

import React from 'react'
import withStyles from 'isomorphic-style-loader/lib/withStyles'
import {Layout} from 'antd'
import withAntStyles from '../withAntStyles'

const {Content, Sider} = Layout

class AppLayout extends React.Component {
  static propTypes = {
    children: PropTypes.node.isRequired,
    return (

export default withAntStyles(AppLayout)

Finally I added the less-loader to my Webpack config:

          // Compile Less to CSS
          // Install dependencies before uncommenting: yarn add --dev less-loader less
            test: /\.less$/,
                loader: 'less-loader',
                options: {
                  modifyVars: {}, // custom theme overrides go here...
                  javascriptEnabled: true