Thursday, February 12, 2015

React JS - Lifecycle and Overview

React JS is a JavaScript library put out by Facebook. I highly recommend taking advantage of using their .JSX files and using their shortcuts and possibly using the Sublime text editor (which has a great free version you can utilize). I will try and share my working knowledge of React JS in hopes it will clear up some things and that I can give someone a basic understanding and "English translation" with practical reasons on using it. This was very different from traditional web layouts and can be hard at first, but you will get used to it! It is very different from a static normal HTML page using CSS and that has some scripts hooked into it. React gives you an entire way to re-design your page around it. You essentially smash all of your HTML and JavaScript together into one JSX file.. call classes from your CSS for styling, and you can most certainly utilize jQuery and other plugins (datetime JS, Charts JS, etc etc..) inside of it. There may be some challenges in working with other plugins because they may work more linearly, and React JS follows a lifecycle with extreme conformity to it, and you have to work everything else around this lifecycle (this, by far, is the most complicated part of using React). This is an extremely long tutorial and will give you all of my experience in using React JS.

React is all about Components. You will build components and put your site together out of these. Think of a component like a reusable block.. maybe even like Minecraft. Minecraft is made entirely out of little blocks. Well, your site too can be made entirely out of little Components (blocks). You have to make a website (Minecraft creation) and it needs things like forms, category drop downs, admin tools, etc (Minecraft grass, wood, trees, etc) so you make ONE form component that has your whole form.. so you build that carefully.. and then whenever you need that form again, all you have to do is place that component in the new spot. So its like making a reusable stamp, or Minecraft block that can be used over and over again and will have the same functionality. By the way.. if you have Razer and model ViewBags and such on your page.. say bye bye to those.. React JS will replace those with its own faster methods of getting data.

The render function is the only REQUIRED function and is essentially the html you will put on your page. Each component can only return (in its render function) one main section/div/container of some kind with many others inside of it (basically.. you HAVE to wrap all your stuff in one bigger container) so React can handle it properly. This is the example off the the React JS main page:
var HelloMessage = React.createClass({
  render: function() {
    return 
Hello {this.props.name}
; } }); React.render(, mountNode);

The actual component is called HelloMessage here. You can see where the component is used like a stamp/Minecraft block here (think of the part putting a whole component name into brackets as the "stamp" that calls this reusable thing):


Reusability! Not only that, but there's flexibility. You can note that there is a "name" property here. You can pass in different things. This is where you want to be careful when constructing your component/stamp/Minecraft block to be as general as you need it and to take in different types of values for properties and be able to deal with it. Passing in functions or values for properties gives you flexibility to utilize or turn on/off features in React.

Let me give you a more practical example of this as a dashboard component example. High level: You have a dashboard and it has a bunch of chart widgets on the dashboard. Dashboard = mommy component. Little charts = individual little baby components. Little baby components only are on the page because dashboard exists, and they can get information (genetics???) from their parent. There will be a parent MyDashboard component which adds child ChartWidget components onto the page. The parent MyDashboard will do an API call to get the attributes of the child (for ex. id/name, size, order etc) Each child component is responsible for its own contents, so based on the id/name its grabbed by from the parent, it will know its identity and do its own API call to get its data (chart or whatever functionality you want). I have taken out much of the nitty gritty functionality of this code to simplify the example and replaced everything with very generic info for the sake of the example.

This is the parent MyDashboard component:
/** @jsx React.DOM **/

var MyDashboard = React.createClass({
    getInitialState: function(){
        return {
            Charts: []
        };
     },
     componentDidUpdate: function(){
         // The debugger statement will let you hit the break point on the browser's developer tools. VERY USEFUL. 
         debugger;
    
         // Just some if else logic to deal with the multiple re-renders
         if (this.initialSetup === true){
            this.funFunction();
            this.initialSetup = false;
         }

         if (this.rerenderCharts === true){
            this.funFunction();
            this.rerenderCharts = false;
         }
    
         if (this.resetCharts === true){
            this.rerenderCharts = true;
            this.resetCharts = false;
            this.forceUpdate();
         }
     },
    funFunction: function(){
        // Function work here
    },
    componentDidMount: function(){
        this.updateFunctions = {};
        this.resetCharts = false;
        this.initialSetup = true;

        // Most of your initial API calls for loading the page will go under componentDidMount 
        $.get('/api/YAYapi', {}, function(graphlist){
            var charts = graphlist.map(function(result){
                return (
                        
                );
             }.bind(this));
        // setting state will cause "charts" to be put into the state variable and then the page will re-render (hit the render function)
        this.setState({Charts: charts});
        }.bind(this));
    },
    render: function(){
        return (
            
Add Chart
{this.resetCharts === false ? (
    {this.state.Charts}
) : }
); }, handleAddChartOnClick: function(){ // functionality for adding a chart }, addWidgetObject: function(widgetName){ var title = ""; var type = ""; // create widget object here }, resetToDefault: function(){ this.resetCharts = true; this.childCallbacks = []; $.get('/api/stuff', {}, function(graphlist){ var charts = graphlist.map(function(result){ return ( ); }.bind(this)); this.setState({Charts: charts}); }.bind(this)); } });


The following is the child ChartWidget component:
/** @jsx React.DOM **/

var ChartWidget = React.createClass({
    getInitialState: function(){
         return {
             ChartData: {}
         };
     },
    componentDidMount: function(){
        var start = this.props.result.start;
        var end = this.props.result.end;
  
        if(this.props.result.DateSelection){
        var dates = this.getDatesFromRange(this.props.result.DateSelection);
        start = dates.StartDate;
        end = dates.EndDate;
        }
  
        this.getChart(start, end, this.props.result.ChartName, this.props.result.ChartType);
    },
    render: function(){
        if (this.props.result.DateSelection){
        var dateRange = this.props.result.DateSelection;
        this.getDatesFromRange(dateRange);
        }
        
        return(
        
  • {this.props.result.ChartTitle}
  • {this.state.ChartData === null ? (No data to display.) : } ); }, getChart: function(startdate, enddate, chartName, chartType){ if(this.oldStart === startdate && this.oldEnd === enddate && this.oldChartName === chartName) return; this.oldStart = startdate; this.oldEnd = enddate; this.oldChartName = chartName; $.get('/api/YAYreports/' + chartName, {start: startdate, end: enddate}, function(chartdatamodel){ if (this.isMounted()) { this.setState({ChartData: chartdatamodel}); this.props.update(chartName, this.updateChart); } }.bind(this)); } });

    The Parent component is the main component that has the Child component () inside of its render function. When the Parent hits the render function, it will then render (stick the HTML of) the child on the page.

    The Lifecycle is the ordering of functions going through in React. In this particular example above here is the order of events:
    1. getInitialState (MyDashboard component): this is only hit the first time the page is loaded and will set your state variables to their initial values. Here, Charts is being set to an empty array.
    2. render (MyDashboard component): the entire section with the class name "main-container" will be put on the page.
      1. The main HTML under the render function will be put on the page. All there is for now is some divs acting as the structure for the page and waiting to be filled in by charts. this.state.Charts is a blank array right now, so as you can see there is a {this.state.Charts} variable trying to render, but there is an if else statement that will put an empty span in its place for this go around in the life cycle.
    3. componentDidMount (MyDashboard component): (the component is mounted, put up, on the page etc).. Invoked once, only on the client immediately after the initial render. You can use the DOM representation of the stuff on the page by using this.getDOMNode(). The ChartWidget component was rendered here... now we setState and this causes a re-render of the MyDashboard component again.. soo.. *also note we did .bind(this) because if you have a function inside of a main React function (render, componentDidMount, or your own..) React can't reach it and it needs to be bound to "this" which puts it on the same layer. When the code hits the part with ChartWidget in brackets (represents the child component. Note there are things being passed into the child.. specifically: result and registerGetParameters. To explain.. result={result} means that the child will have a property called this.props.result which will have all access to everything that was assigned to result in the parent. The {result} in brackets, well, result will be a local variable in this case that has the value to assign to the child. If you had passed in abc={def} then the child will have this.props.abc to access the parent's local variable def. registerGetParameters here is a function being passed to the child. this.registerGetParameters represents the function within the component that is called registerGetParameters, and attaching "this." in front of it will allow you to access any functions within the same component. Also..this.setState({Charts: charts}) causes the current parent component to re-render because you called the this.setState function. Note, simultaneously.. you set this.state.Charts to the little "c" charts which comes from api/YAYapi. This will cause a lifecycle in the ChartWidget component as follows to occur next:
    4. render (MyDashboard component, again): we will now render the page again, but this time we have the info from the API call set in the this.state.Charts variable (see where we called setState({Charts: charts})). Remember that if else statement that put an empty span into all those holding divs? Now, this.state.Charts is an array filled with all kinds of cool things from your API call that was done in api/YAYapi. So, remember this.state.Charts came from a variable in componentDidMount's little "c" charts variable which returned another component.. this is our little child component ChartWidget, which is now in state and will be rendered onto the parent! It will be rendered once per chart and each chartWidget component will go through the following lifecycle of its own:
      1. getInitialState (ChartWidget component): ChartData is an object and will by default be an empty object when the component is first rendered.
      2. render (ChartWidget component): The state for this.state.ChartData is currently just an empty object, and will not render. Only the outside HTML will render and all the values with this.props will be filled in because it comes from the parent.
      3. componentDidMount (ChartWidget component): Invoked only once PER CHART. this.props.result holds the value of result from the parent that was passed into this child. In this case, result has the title of the chart, its name/id, and other info. Here, the function this.getChartValues will be called, which goes to that function, which will do an API get call. this.setState({ChartData: chartdatamodel}); is called and will cause the child ChartWidget component to re-render with state... (looking familiar?)
      4. render (ChartWidget component, again): Now, the data from the API get call will be rendered onto the page and come from the variable stored in state (this.state.ChartData).
      5. componentDidUpdate (ChartWidget component): "Invoked immediately after the component's updates are flushed to the DOM. This method is not called for the initial render." - The React JS website said it best! More functionality here basically..
        THE ABOVE WILL HAPPEN ONCE PER CHART. Each chart is responsible for itself and its own stamp of the child component. If you have 6 charts, each chart will go through this process once per chart. This renders the actual HTML of the chart and fills in the data with the info from each chart's API call. If you have 6 charts, each chart will make its own API get call. Once the charts have all rendered one by one, we will now go back to the parent..
    5. componentDidUpdate (MyDashboard component): more functionality here.. this is some if/else logic used to deal with if this is the first time rendering or not and what to do. Mainly I used it here to put add/remove/reset chart functionality but leaving it out of this tutorial for simplicity! Just know that this comes after render and is done everytime render is called except the first/initial render.
    State and Props: State is something dynamic that changes on the page.. everytime you set State.. you will cause the page to re-render. React is smart in the way that it will not just rerender your whole page everytime.. it only rerenders what it needs to. It makes the least amount of changes to the DOM to get page A to look like the new page B. Things to store in state would be your actual charts that need to be rendered. Props are usually static variables or functions or whatever you need to pass down to a child to get them to look/feel/function a certain way. Everything that is passed into a child component will become a prop. State is used inside the current component, and you should set an initial value for state in the getInitialState function. More here on state and props directly from React: http://facebook.github.io/react/docs/tutorial.html

    Note, classes shall henceforth be known as className now. Change EVERY single class to className. React will break otherwise. Id's are essentially.. gone now.. so wrap your CSS well or name your classes very uniquely do that they don't interact at a later time. Ok well Id's do exist, but don't use them, it gets messy and React is build on using className everywhere. Also on styling your CSS.. while you CAN do inline styles in a "React" type of way.. this is typically messy and a bad idea.. so make you CSS neat and correspond to your className and everything will be ok. Takeaway: don't try and put id or inline CSS on React. Make yourself another class/className and add the styles directly into your CSS file. You will make your CSS file exactly how it was before, with the "." and all before the className (synonymous with if you just had a HTML page and a class on the page). If you need to.. make some global variables like "orange" and "blue" classes in your CSS and add that "orange" or "blue" to your className list inside your HTML in your components if it seems silly to have a class just for one little thing. Having well ordered CSS is a MUST when using React or things will deteriorate quickly into a jumbled CSS spaghetti soup.

    This get a little more complex as you add your own functions to say.. add or remove a widget which will do an API POST call and also need to change what's rendered on your page. You will go through parts of this lifecycle again.. the render and componentDidUpdate each time you do this.

    Functions.. Also, if you switch pages, you may have a need for nextProps. If you need to call a function within the same component, you will call this.functionName and be able to intialize it / access it / pass it into a child component as a prop.

    Mixins are add ons that are available or that you can make your own to add some extra functionality. You need to determine if what you are making should be a component or a mixin. A component is a self sufficient little block. A mixin is usually a functionality you want to add on to a component. You can use these to do two way binding in React for example.

    A few random tips:
    • Global variables (global within your component) can be declared and used anywhere in the component. You simply call this.variableName and assign its value and use it anywhere afterwards in the life cycle.
    • If you use a JSX file, you have to put /** @jsx React.DOM **/ on the top.
    • Refs, and using refs in such a way like this.ref.refName.getDOMNode can be used kinda like jQuery.. you can functionally do it either way, its the same thing.
    • .bind(this) will absolutely kill you and stump you and make you life a misery if you forget to use it.
    • Setting State using React's built in function: this.setState({Charts: chart}). You can also manually set state and then call this.forceUpdate() which will then cause the rerender.
    • Write "debugger;" without the quotes where you need a breakpoint. It'll save you SO MUCH TIME. :D