Dataset in React

When life gives you lemons, you make lemonade. And when it doesn’t, hack your way around it. Such was the case with a piece of React code where the previous programmer passed some data-* attributes as props, then onto the JSX markup and later extract them from event.target in the event handlers (or also from the event.currentTarget or from native event). Now, React isn’t ready enough for this (as of January, 2017). The dataset property which are generally accessible via the HTMLElement.dataset property aren’t handled the DOM way.

While playing around wrappers, I had to find a workaround to pass the data-* props. I wrote a little hack to extract the data-* attributes using regex and some spread syntax to pass the dataset to both the outer wrapper, and the inner component.

Example:

render() {
  // Extract the keys present in the `props`
  // Filter the keys which have `data-` as prefix
  // and insert them into `dataset` object
  const dataset = {};
  const { state, props } = this;
  Object.keys(props).filter (value => {
    if (/data-\S+/gi.test(value)) {
       dataset[value] = props[value];
    }
  });
  return (
    <Wrapper {...dataset} onChange={this.handleChange} >
      <Component {...dataset} name="component-type-1">
      </Component>
    </Wrapper>
  )
}

The advantage – you can access the dataset in your handleChange method from the event.currentTarget and event.target. Visit this MDN link to know more about dataset.

Redux forms: Scroll to form field containing error message

  • Redux forms v6.2+ provide you with an onSubmitFail callback when a validation fails in your form.
  • Add refs to all Field elements with name
  • Note: Without refs, this will not work at all.
  • Import the following function as a utility and bind it in your constructor.
  • Whenever there is an error, this scroll the page to the 1st element in the page containing errors.

 


import ReactDOM from 'react-dom';
/**
 * Handles the errors received and determines the first element
 * containing the error as the 'key' element, so that,
 * it's positions can be determined
 * Scrolls the page that element to bring into visibility
 * And focuses on the element
 * Requires developers to use 'ref' attribute with 'name' in form fields
 * @param  {[object]} errors
 * @return {[undefined]}
 */

export function handleSubmitFail(errors) {
    if (!this.refs) {
        return;
    }
    const refsKeys = Object.keys(this.refs);
    const keys = Object.keys(errors);
    let key = null;
    let matchfound = false;

    refsKeys.filter(item => {
        if (keys.indexOf(item) > -1 && !matchfound) {
            key = item;
            matchfound = true;
            return false;
        } else { // eslint-disable-line no-else-return
            return true;
        }
    });

    this.targetNode = this.refs[key];

    if (this.targetNode) {
        const node = ReactDOM.findDOMNode(this.targetNode);
        const parentNode = ReactDOM.findDOMNode(this);
        const xy = node.getBoundingClientRect();
        this.x = xy.right + window.scrollX;
        this.y = xy.top + window.scrollY - 60;
        parentNode && parentNode.scrollTo && parentNode.scrollTo(this.x, this.y) || 
        (parentNode && parentNode.scrollTop && (parentNode.scrollTop = this.y) ) || 
        window && window.scrollTo(this.x, this.y); // eslint-disable-line no-unused-expressions
    }
}

 

Make a Custom Array without distorting the Original

  function MyArray () {
    var _arr = [].slice.call(arguments, 0);
    for( var i=0; i<_arr.length; i++ ) {
      this[i] = _arr[i];
    }
    var THIS = this;
    var max = arguments.length;
    Object.defineProperty(THIS, 'length', {
      enumerable: false,
      get: function() { 
        var keys = Object.keys(this);
        for(var i=0; i< keys.length; i++) {
            keys[i] = parseInt(keys[i], 10);
            if( !isNaN (keys)[i] ){
              if (keys[i] > max) {
              max = keys[i]
            }
          }
        }

        for( var i=0; i<max; i++ ) {
          if( !this[i] ) {
            this[i] = void 0;
          }
        }
        return max;
      },
      set: function(){
        max = arguments[0];
      }
    });


    this.splice = [].splice.bind(this);
    this.push   = [].push.bind(this);
    this.add    = [].push.bind(this);
    this.splice = [].slice.bind(this);
    this.pop    = [].pop.bind(this);

    this.addAll = function() {
      var arr = arguments[0];
      for (var i=0, j = this.length; i<arr.length; i++, j++) {
        this[j] = arr[i];
      }
    };
    return this;
  };


  MyArray.prototype = new Array;
  MyArray.prototype.constructor = MyArray;



  var collection = new MyArray(1, 2, 3, 4);


  console.log(collection);
  //true
  console.log(collection instanceof Array);
  //true
  console.log(collection instanceof MyArray);
  // true

  console.log("collection", collection);
  //[1,2,3,4];
  console.log("collection.length", collection.length);
  //4

  console.log("-------------------------1");


  collection.add("hello");
  console.log("pushed hello");
  //5
  console.log("collection.length", collection.length);
  //5
  console.log("collection[4]", collection[4]);
  //"hello"
  

  console.log("-------------------------2");


  collection.push("world");
  console.log("pushed world");
  console.log("collection.length", collection.length);
  //6
  console.log("collection[5]", collection[5]);
  //world
  console.log("collection", collection);
  //world



  console.log("-------------------------3");


  collection.addAll(["java", "script"]);
  console.log("pushed [java, script]");
  console.log("collection.length", collection.length);
  //8
  console.log("collection", collection);
  //[1,2,3,4,"hello", "world", "java", "script"]


  console.log("-------------------------4");



  collection[8] = "last";
  console.log("assigned collection[8] = 'last'  ");
  //9
  console.log("collection.length", collection.length);
  //9


  console.log("-------------------------5");



  console.log("collection.pop()", collection.pop());
  //"last"
  console.log("collection.length", collection.length);
  //8
  console.log("collection", collection);
  // [1,2,3,4,"hello", "world", "java", "script"]


  console.log("-------------------------6");
  console.log("Array.prototype.addAll", Array.prototype.addAll);
  //undefined


Creating a Guitar Tuner – With modern web APIs

String Frequency Scientific pitch notation
1 (E) 329.63 Hz E4
2 (B) 246.94 Hz B3
3 (G) 196.00 Hz G3
4 (D) 146.83 Hz D3
5 (A) 110.00 Hz A2
6 (E) 82.41 Hz E2

This table can be obtained from the Guitar Tuning wiki page.

So, this is how it goes:

  1. Our objective here is to generate the frequencies when requested, like when a user presses a button
  2. We make use of the Web Audio API to generate the frequencies listed above
    • An extension to it, will be using the micro phone to match the frequencies, but that won’t be covered in this part
    • Paul Lewis has an excellent app built with the above approach
  3. To use the web api, we must create an instance of the AudioContext object
    • Akin to canvas, we must instantiate an audio context object before accessing the web audio api
    • And, to generate the frequencies, we have to create an oscillator.

// create web audio api context
var audioCtx = new (window.AudioContext || window.WebkitAudioContext);

// create Oscillator node
var oscillator = audioCtx.createOscillator();

Now, we’ve to specify the type of wave. These are four natively supported types:

  • sine
  • square
  • sawtooth
  • triangle

A custom type is also available for use. We are not getting into that.

  • We’ll use the sine wave, because that audio wave is bearable.
  • We’ve to set a frequency value at which the oscillator will produce the waves. Let’s set it to E1, i.e. 329.63 Hz
  • We’ve to connect to the destination supported by the Audio Context. The output generally the standard audio interface i.e. your speakers.
  • Next, we start the oscillator.
  • Remember, the oscillator can be started once, and only once. It can be stopped, but can’t be restarted.
  • If you make live changes to the frequency or the type of the wave, the changes are reflected in the oscillator realtime. Hence, the absence of a restart functionality won’t be felt much.

Let’s create an oscillator React component (sorry, p-react, because size matters).

Now, the markup in the snippet ahead appeared gibberish. Therefore, I’ve posted a gist instead.


# oscillator.js
import { h, Component } from 'preact';
import style from './style';

const audioContext = new (window.AudioContext || window.webkitAudioContext);

export default class Oscillator extends Component {

  play() {
    this.oscillator = audioContext.createOscillator();
    this.oscillator.type = this.props.type || 'sine';
    this.oscillator.frequency.value = this.props.frequency || 329.63; //E(1) is default
    this.oscillator.connect(audioContext.destination);  
    this.oscillator.start();
  }

  stop() {
    this.oscillator.stop();
    this.oscillator = null;
  }

  render() {
    return ( /* refer the gist */ );
  }
 }

 

The reasons for creating a new instance every time you hit the start button are

  • The start method only works once per oscillator. Hence, once stopped, there is no way to restart the oscillator.
  • There is no API to suspend and later resume the oscillator.
  • The context can be suspended and resumed later but that doesn’t stop the oscillators. And, when you resume the context after firing multiple oscillators, you hear all of them buzz simultaneously.
  • Therefore, we must create a new oscillator instance then start, every time we hit start and stop-then-destroy the instance every time we hit the stop button.

Now, we’ve to make some buzzing & humming by assigning values to the props. If you can’t see the code here, then follow this gist.

  <Oscillator note="E1" frequency="329.63" type="sine" />
  <Oscillator note="B2" frequency="246.94" type="sine" />
  <Oscillator note="G3" frequency="196.00" type="sine" />
  <Oscillator note="D4" frequency="146.83" type="sine" />
  <Oscillator note="A5" frequency="110.00" type="sine" />
  <Oscillator note="E6" frequency="82.41" type="sine" />

And, we're done.

Caution

Make sure to lower your speaker volumes. If you’re using head phones, then definitely cross check 3 times if your volume is low or not. I don’t want people testing it go all Beethoven on the first day.

Hit the start/stop buttons and tune your guitar along.

Chords

In the similar fashion, we can create a chord component (possibly in the next tutorial) that creates 3 oscillators and plays all of them simultaneously to create a resonating chord.

Hint: A frequency combination for C-major are 196.00 (G), 261.63(C) and 329.63(E). And, for creating a G-major, you can use a combination of 146.83(D), 196.00(G),  and 246.94(B).

happy humming

More

 

Run a little gem: Faker

Faker @github is a ruby gem for generating fake data, albeit not guaranteed to be unique every time. It’s for general purposes of fake data generation.

# Instructions
# go to terminal
$ gem install faker

# start the interactive ruby shell
$ irb

# load the gem: Faker
irb(main):011:0> require 'Faker'
true
# returns 'true' if you're loading it for the first time.
irb(main):011:0> require 'Faker'
false
# false, because it's already loaded

# now, time to play around
irb(main):011:0> Faker::Name.name
=> "Brook Reichel"

irb(main):011:0> Faker::Address.city 
=> "West Brigitte"

# need a bitcoin wallet?
irb(main):014:0> Faker::Bitcoin.address
=> "1PfXhC3E64DYkdmYPKGwxziRh3ZzMKgzNn"


Update 1:

On the similar premises there are some other data generators you can have at your disposal

How the memory usage shoots up in a VPS when you create virtual hosts

screen-shot-2016-09-23-at-3-54-01-am

The server was running on port 80 and serving a single domain on the VPS. I `a2ensite`-ed  2 domains  running on different ports. Mind you, the server was rendering nothing beyond static html files containing `hello world` messages.

You can see the server running calmly at 2.5MBs and after creating those domains, the memory rises beyond 12.5MBs.

That’s nearly 6 times.

In the coming weeks I’ll hunt for some data around this to present the full case: memory usage with virtual hosts.

Related articles:

 

Bookmarks: September (╯°□°)╯︵ ┻━┻

Great Quotations

On being master of many trades and desist being a stagnant specialist of one trade

The devout specialist is fond of labelling the impetuous learner as an incompetent imposter – Tim Ferris

The 80-20 rule

20% of a language’s vocabulary will enable you to communicate and understand at least 80% of the framework

About Gartner

Those who can do, do. Others Gartner.

And console.log this
`to %c🚀 ${'infinity'} %c ${'and'} %c ${'beyond'}`, 'color:#F88;', 'color:#88F;', 'color:#1B9;'

Why did you underline that text in the book? Everything is important in the book. – My Geography teacher, Nayak Sir

He was right. On the internet, everything feels important and there is FOMO and fear of losing information associated. You can never have it all. That’s saddening and that suddenly reminded me of my Geography teacher.


  • Shell script syntax highlight – search for this sublime plugin and update the urls

DPI specific image loading ( Not srcset )

HTML5 provides you with srcset attribute on img tags to load resolution-, dimension- or dpi- specific images. But techniques for loading higher DPI images existed since the early days of HTML5 + CSS3 announcement.

Mobile web app developers, especially the ones developing for iOS safari, have been using -webkit-device-pixel-ratio in media queries to load up normal images for non-retina and images of higher dimension for retina display, squeezed into the same space by adjusting background-size property.

The same can be achieved for loading images on a standard page

  1. Set the image src to a transparent 1×1 gif , Data URIs preferred (albeit slow, but we’re talking about a workaround remember?)
  2. Set the dimension of the images via css (also attributes, because performance)
  3. Write/Generate some CSS
  4. Set the background image for the img tag

The only barrier in this approach – dynamic images: Ideally people don’t change the master CSS file for changes in banner images.

The easiest workaround for this problem: Dynamically injected inline CSS straight into the markup that contains the image URLs for the banner images.

MySQL Error in 5.7+: only_full_group_by

Generally found in: v5.7+
Version on my machine: 5.7.14
Key words: only_full_group_by, 
Remedy:
Documentation: 6.1.7 Server SQL Modes

Solution

The issue is generally observed in 5.7+. If your team/ you previously worked with an older version, then it’s advisable to stay consistent with the stack. However, in specific cases where you can’t downgrade the version, this solution may work.

Disable FULL_GROUP_BY Mode temporarily:

You may need to execute this every time you do a fresh war deployment.

# execute the following commands
mysql> set global sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
mysql> set session sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';

Change MySQL Conf to permanently change SQL mode in MySQL:

First we need to identify the location of the configuration file.

Note: If you’re on Mac, the binary mysql installer (not homebrew) doesn’t create a my.cnf file. You can find sample conf files in the support-files directory located under /usr/local/mysql/support-files.

You can find the cnf file by:

$ which mysqld
# let's say it gives you /usr/sbin/mysqld
$ /usr/sbin/mysqld --verbose --help | grep -A 1 "Default options"
# It'll give the following output
# Default options are read from the following files in the given order: 
# /etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf

# The cnf file doesn't appear in the default non-home-brew installation
# The sample file can be found under the following directory

$ which mysqld
# let's say it gives you /usr/sbin/mysqld
# cd .. to 2 levels back to the mysql directory
# Try to locate the 'support-files' directory

$ cat /usr/sbin/support-files/my-default.cnf
# This contains a sample cnf file
# copy the contents and create one file under the user directory

$ touch ~/my.cnf

Make the following changes to the my.cnf file:

[mysqld]
sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

 

Although not required, restart your MySQL server as a precautionary measure.

webpack sucks, at least for now

Javascript is an interpreted language and by that I understand that when I hit the F5, I expect zero latency for the new script to appear in local dev environment.

When you take that away by introducing obnoxious compilers like webpack – that first needs to be told how to load, then it compiles, then concats before showing me the output, you’re already the subject of my fury.

The ‘wait time’ for compilation sucks

The reasons why I stayed all these years away from coffee script –

  1. I know how to write ‘good’ javascript and
  2. The compilation time – it sucks.

Let JS remain the interpreted language we all know and love. Don’t put your compiled language genius into it. You’re not welcome here – to the interpreted world.

Debugging is horror

you’ve to trace that line out. Imagine a project consisting of 100 small files with almost similar looking content that you concatenated and now clueless where exactly to hunt and debug.

It’s not uncommon – when you write derived components inherited from parent components, the siblings tend to look similar.

webpack – you’re a clutter builder and clarity killer. And, No. I’m not going to work in large files a.k.a monoliths just to support your existence.

Lack of build blocks

I came from an AngularJS Development environment, where I extensively used yeoman and that allows me to work on an index.html file locally which has references to locally kept css and JS.

That means we don’t have to wait for concat or compile before we hit the F5 or ctrl+r.

Plus, the library files from bower_components stay separate. In webpack, unfortunately, they become part of the compilation step.

Luckily, we’ve wiredep and usemin blocks for our rescue – which simplifies local development and gives great support for production builds.

Learn something from it. Your hotness may look tempting to fools and noobs. I ain’t one. Grow up.

Till then – happy hating.

Remedy

And, I always believe there is no point in complaining, one must find a remedy. Following are some workarounds to reduce frustration: