2/26/2017

React Decorator Example - Button Permission

In our application, every button's visibility is determined by the the user's role. For example, if the user's role is guest, they have no permission to create a project.
We get the permission list via ajax to decide whether each buttons is displayed or not. The traditional way to archive it is that, in render function, determine whether the button is displayed according to the user's permission.
class Page1 extend React.Component {
 render() {
  const {create, update} = window.globals.permissions;
  return (
   {create && <Button>Create Project</Button>}
   {update && <Button>Update Project</Button>}
  );
  
 }
}
It works fine, but, if there's another page called Page2 which contains some buttons like Page1. We have to write the similar code in the Page2 again. Furthermore, if the requirement changed: "Disable the button if user have no permission". Then we have modify all pages like this:
class Page1 extend React.Component {
 render() {
  const {create, update} = window.globals.permissions;
  return (
   {<Button disabled={!create} >Create Project</Button>}
   {<Button disabled={!update} >Update Project</Button>}
  );
  
 }
}
Here comes the Decorator Pattern. Since button's visibility or disability is only determined by the permission key, could we just set a property in the Button component to check it? The permission check, visibility or disability property is done by decorator.
class Page1 extend React.Component {
 render() {
  return (
   {<Button auth="create">Create Project</Button>}
   {<Button auth="update">Update Project</Button>}
  );  
 }
}
Here is the decorator:
export default function injectAuth(component) {
    const checkAuth = (props) =&gt; {
      let {auth} = props;
      // No Auth required
      if(auth == null) {
        return true;
      }
      return windows.globals.permissions[auth];      
    }

    let WrappedClass = class extends component {

      render() {
        if(checkAuth(this.props)) {
          return super.render();
        }
        else {
          const noAuthType = this.props.noAuthType || 'hidden';
          if(noAuthType === 'hidden') {
            return null;
          } else if(noAuthType === 'disabled') {
            // React doesn't allow to modify props like this.prpos.disabled=ture
            // Should clone the element to with inital props
            // See: http://stackoverflow.com/questions/32370994/how-to-pass-props-to-this-props-children
            return React.cloneElement(super.render(), {disabled:true});
          } else {
             throw new Error('noAuthType必须是hidden或disabled');
          }

        }
      }
    }

    return WrappedClass;
}
injectAuth(component) is a function with parameter component class which returns a new wrapped class. The wrapped class extends the parameter component so that all the properties and method is inherited. In the wrapped class's render function, it check if the auth property exists in windows.globals.permissions. If yes, just returns the component's render result.
return super.render();
If no, return null or disabled component according to the noAuthType props.
Finally, inject your button component like this:
class Button { 
 ...
}
export default injectAuth(Button);

11/23/2016

[CSS] Center a Position Absolute Element

Here's quite simple solution, but only work on IE >= 9.
position: absolute;
margin-left: auto;
margin-right: auto;
left: 0;
right: 0;

9/13/2016

[SVG] Horizontal Line not Visible in Chrome


<line
  x1="10" y1="1"
  x2="90" y2="1.0001" // hack: horizontal line in SVG not visible in Chrome
  stroke="#FF0000"
  strokeWidth="1"/>


One easy way to fix it is just make it not perfectly horizontal. Adding a little bit offset makes it visible.

SVG Path Percentage Units

  <svg width='100%' height='100%' viewBox="0 0 100 100" preserveAspectRatio="none" style='background-color: whitesmoke'>
   <path  stroke="#000" fill="none" d="M0,0 L50,50" />
  </svg>  
Using percentage in svg tag, then the element inside it will be in percentage unit.

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);
  }