7/02/2016

[React] Don't Use Loop Index as Component Key

var ResultList = React.createClass({
  getInitialState: function() {
    return {results:[{id:1,text:'hello'}]};
  }, 
  onclick: function() {
    this.setState( {results:[{id:1},{id:2}]} );
  }, 
  render: function() {
    return (
      <div> 
   <button onClick={this.onclick} >click me!</button>
      <ul>
        {this.state.results.map(function(result, i) {
           return <ResultItem result={result}/>;
        })}
      </ul>
      </div>
    );
  }
});

var ResultItem = React.createClass({
  getInitialState: function() {
    return {clicked:false};
  },
  onclick: function() {
    this.setState( {clicked: !this.state.clicked } );
  }, 
  render: function() {
    return (
      <li onClick={this.onclick}>{' [clicked state] =  ' + this.state.clicked}</li>
    );
  }
});

ReactDOM.render(<ResultList />, mountNode);
If you render a list of components in a loop, don't forget to add key attribute to each of the component or React will give you a warning message like: "Each child in an array should have a unique "key" prop. Check the render method of undefined. See http://fb.me/react-warning-keys for more information".
To solve this problem, many people may just make a loop index as component key as below. Well, this solution does make the warning message disappear. However, it's an anti-pattern and will make your stateful components have incorrect state.
{this.state.results.map(function(result, i) {
  return <ResultItem key={i} result={result}/>;
})}
React's diff algorithm will try to find the min steps to add, remove, and update component's state in O(n) time. When it detects the component's class name (i.e., ResultItem) is the same and have the same key, it assume the component instance is the same one. Therefore, React will not update this component's state. So when the button is clicked in ResultList, you may think the ResultItem's result props is changed, then ResultItem will be re-inializated with the initial state {clicked: false}. It's not true. Since React think the ResultItem with id = 1 is the old instance, the state still remains the old state.
The correct to add a key to component is just use the unique id in the database like:
{this.state.results.map(function(result, i) {
   return <ResultItem key={result.id} result={result}/>;
})}
It's noted that the key should be added to a component's props instead of added to the children's HTML. Otherwise, React's diff algorithm will not know the children component's id.
// WRONG!
var ResultItem = React.createClass({
  render: function() {
    return (
      <li key={this.props.result.id}></li>
    );
  }
});
Ref: https://facebook.github.io/react/docs/multiple-components.html#dynamic-children

[React] Don't Use Loop Index as Component Key

var ResultList = React.createClass({
  getInitialState: function() {
    return {results:[{id:1,text:'hello'}]};
  }, 
  onclick: function() {
    this.setState( {results:[{id:1},{id:2}]} );
  }, 
  render: function() {
    return (
      <div> 
   <button onClick={this.onclick} >click me!</button>
      <ul>
        {this.state.results.map(function(result, i) {
           return <ResultItem result={result}/>;
        })}
      </ul>
      </div>
    );
  }
});

var ResultItem = React.createClass({
  getInitialState: function() {
    return {clicked:false};
  },
  onclick: function() {
    this.setState( {clicked: !this.state.clicked } );
  }, 
  render: function() {
    return (
      <li onClick={this.onclick}>{' [clicked state] =  ' + this.state.clicked}</li>
    );
  }
});

ReactDOM.render(<ResultList />, mountNode);
If you render a list of components in a loop, don't forget to add key attribute to each of the component or React will give you a warning message like: "Each child in an array should have a unique "key" prop. Check the render method of undefined. See http://fb.me/react-warning-keys for more information".
To solve this problem, many people may just make a loop index as component key as below. Well, this solution does make the warning message disappear. However, it's an anti-pattern and will make your stateful components have incorrect state.
{this.state.results.map(function(result, i) {
  return <ResultItem key={i} result={result}/>;
})}
React's diff algorithm will try to find the min steps to add, remove, and update component's state in O(n) time. When it detects the component's class name (i.e., ResultItem) is the same and have the same key, it assumes the component instance is the same one. Therefore, React will not update this component's state.
So when the button is clicked in ResultList, you may think the ResultItem's result props is changed, then ResultItem will be re-inializated with the initial state {clicked: false}. It's not true. Since React think the ResultItem with id = 1 is the old instance, the state still remains the old one.
The correct to add a key to component is just use the unique id in the database like:
{this.state.results.map(function(result, i) {
   return <ResultItem key={result.id} result={result}/>;
})}
It's noted that the key should be added to a component's props instead of added to the children's HTML. Otherwise, React's diff algorithm will not know the children component's id.
// WRONG!
var ResultItem = React.createClass({
  render: function() {
    return (
      <li key={this.props.result.id}></li>
    );
  }
});

Ref: https://facebook.github.io/react/docs/multiple-components.html#dynamic-children

1/05/2016

Loading CSS Styleheet in IE8, E7, and IE6 Dynamically

Since innerHTML doesn't work in IE8 and below. Here is a hack for inserting CSS Styleheet in IE8 using javascript programmatically.
  var css = 'body {color:red}';
  var style = document.createElement('style');
  style.setAttribute("type", "text/css");
  var head = document.getElementsByTagName('head')[0];
  head.appendChild(style);
  if (style.styleSheet) {   // IE
    style.styleSheet.cssText = css;

  } else {  // modern browser
    var node = document.createTextNode(css);
    style.appendChild(node);
  }

10/21/2014

[ExtJS] Remove Fake Path in File Field Across Browsers

Here's an example for file uploader with Ext JS. Whenever users pick a file, the file field will fire a change event with the arguments (field, value).
Ext.create('Ext.form.Panel', {
    title: 'Upload a Photo',
    width: 400,
    bodyPadding: 10,
    frame: true,
    renderTo: Ext.getBody(),
    items: [{
        xtype: 'filefield',
        listeners: {
            change: function(field, value) {
                console.log('value', value);
            }
        }
    }]
});
According to the document, the getValue method will return a value that is browser-dependent; some have just the file name, some have a full path, some use a fake path.
For example, in Chrome, the output in the snippet above is:
value C:\fakepath\myfile.txt 
The same code in Firefox will print:
value myfile.txt 
How if we just get want to get the file name instead of the fake path across browsers? That is, we intend to have the following output in all browsers.
value myfile.txt 
All you have to do is just override the filefield class with the following code.
In Ext JS 5,
Ext.define('Ext.enhance.form.field.File', {
    override: 'Ext.form.field.File',     
    onFileChange: function(button, e, value) {
        this.duringFileSelect = true;
        Ext.form.field.File.superclass.setValue.call(this, value.replace(/^.*(\\|\/|\:)/, ''));
        delete this.duringFileSelect;
    }   
});
In Ext JS 4,
Ext.define('Ext.enhance.form.field.File', {
    override: 'Ext.form.field.File',
    onFileChange: function() {
        this.lastValue = null;
        Ext.form.field.File.superclass.setValue.call(this, this.fileInputEl.dom.value.replace(/^.*(\\|\/|\:)/, ''));
   }
});
Please use Chrome to test this overriding in the fiddle.

10/19/2014

[JavaScript] function scope and block scope

In many languages such as C, C++, and Java, the scope is defined by a block. In the following Java example, the variable x in the inner scope is not accessible by outer scope.
for(int i=0;i<10;++i) {
    int x = 1;
}
System.out.println(x); // error
However, in Javascript, scope is defined by a function.
In the following sample, variable x is defined in the loop block which doesn't create a new scope while variable y is defined in a function which create a new scope. So, The foo function is in inner scope and global window is in outer scope.
Since variable x is in the outer scope, the function foo is able to access it. And variable y is in the inner scope, we can't access it in outer scope (window).
var i;
for(i = 0; i < 10; ++i) {
  var x = 1;
}

// function will create a new scope   
function foo() {
  var y = 1;
  x++;
}

console.log('x', x); // 1
foo();
console.log('x', x); // 2
console.log('y', y); // Uncaught ReferenceError: y is not defined

10/16/2014

[ExtJS 5] Migrate from ComponentColumn to WidgetColumn

In Ext 4, we use componentcolumn to render Ext component in a grid cell.
{
  dataIndex: 'age',
  xtype: 'componentcolumn',
  renderer: function(value, m, record) {
    if (value < 30) {
      return {
        xtype: 'myWidget1';
      };
    } else {
      return {
        xtype: 'myWidget2';
      };
    }
  }
}
In Ext 5, the built-in widgetcolumn have the same ability to do so. But the widgetcolumn have better rendering performance.
{
  xtype: 'widgetcolumn',
  widget: {
    xtype: 'container',
  },
  onWidgetAttach: function(widget, record) {
    var items = [];
    if (record.get('age') < 30) {
      items.push({
        xtype: 'myWidget1'
      })
    } else {
      items.push({
        xtype: 'myWidget2'
      })
    }  
    widget.removeAll();
    widget.add(items);
  }
}
Note that onWidgetAttach() is a public but not documented method.

10/10/2014

[Javascript] Event Bubbling and Capturing Propagating Order

Problem

We have three elements in the DOM shown below. When users click on the element3, all of elements' click handler will be executed. But what's the order of executing handler?
<div class="element1">element1
    <div class="element2">element2
        <div class="element3">element3</div>
    </div>     
</div>

The W3C Event Bubbling and Capturing Propagating Order

According to W3C standard, there are two major phases for propagating event object.
Capturing phase:
Firstly, The event object will propagate through the target's ancestors from the Window to the target's parent (Note that target in this case is element3.)
Bubbling phase:
Then event object propagates through the target's ancestors in reverse order, starting with the target's parent and ending with the Window.
Event Bubbling and Capturing Propagating Order
Event Bubbling and Capturing Propagating Order
So let's add some event listeners to print out the propagating order of event object. Before that, let's see the arguements of addEventListener.
/**
  * @param {String} type The event type
  * @param {Function} listener The event handler
  * @param {Boolean} useCapture If True, use capturing. Otherwise, use bubbling.
  */
  element.addEventListener(type, listener[, useCapture]);
The following code make all elements register a click event for both of bubbling and capturing. Note that registering an event for the same listener doesn't affect the order of event object propagating.
var element1 = document.getElementsByClassName('element1')[0],
    element2 = document.getElementsByClassName('element2')[0],
    element3 = document.getElementsByClassName('element3')[0],
    foo = function(e) { console.log(this.className); };

element1.addEventListener('click', foo); // bubbling
element1.addEventListener('click', foo, true); // capturing
element2.addEventListener('click', foo);
element2.addEventListener('click', foo, true);
element3.addEventListener('click', foo);
element3.addEventListener('click', foo, true);
When you click element3, it will show
element1
element2
element3
element3
element2
element1 
As we expected, the event object will first propagate in capturing phase (top-down) and  then bubbling phase (bottom-up).
By the way, you can stop the propagation by calling e.stopPropagation().
var stop = function(e) { 
    console.log('stop', this.className); 
    e.stopPropagation();
};

element1.addEventListener('click', foo);
element1.addEventListener('click', foo, true); 
element2.addEventListener('click', stop); // Change handler to "stop"
element2.addEventListener('click', foo, true);
element3.addEventListener('click', foo);
element3.addEventListener('click', foo, true);
The output
element1
element2
element3 
element3
stop element2 
The Playground in JS fiddle.