Skip to content

Instantly share code, notes, and snippets.

@concerned3rdparty
Forked from anantja-in/React Notes.md
Created September 28, 2015 16:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save concerned3rdparty/18b38b4e232175255126 to your computer and use it in GitHub Desktop.
Save concerned3rdparty/18b38b4e232175255126 to your computer and use it in GitHub Desktop.
These notes were taken while watching React videos at https://egghead.io/. Most of the code is from the videos there.

#Introduction to Properties

<script type="text/jsx">
    /*** @jsx React.DOM */
    var APP = React.createClass({
        getDefaultProps:function(){
            return {
                txt:'gdfgdfgdf',
                cat:0
            }
        },
        propTypes:{
            txt:React.PropTypes.string,
            cat:React.PropTypes.number.isRequired
        },
        render:function(){
            console.log(this.props.txt)
            return (
                    <div>
                        <h1>{this.props.txt}</h1>
                        <b>bold</b>
                    </div>
                    )
        }
    });

    React.renderComponent(<APP cat={5} />,document.body)
</script>

Things to note:

  1. Number props need to be sent in {}, strings do not
  2. Way to specify what's expected and/or required
  3. Way to specify default props

State Basics

Why?

Properties are supposed to stay static. But if they're going to be changed, use state

var APP = React.createClass({
        getInitialState:function(){
            return {txt:'',id:0}
        },
        updateTxt:function(e){
            this.setState({txt:e.target.value})
        },
        render:function(){
            return (
                        <div>
                            <input type="text" onChange={this.updateTxt} />
                            <h1>{this.state.txt}</h1>
                        </div>
                )
        }
    });

Things to note:

  1. How to assign initial state
  2. How to update state

Owner Ownee Relationship

var APP = React.createClass({
        getInitialState:function(){
            return {txt:'',id:0}
        },
        updateTxt:function(e){
            this.setState({txt:e.target.value})
        },
        render:function(){
            return (
                        <div>
                        <Widget txt={this.state.txt} update={this.updateTxt} />
                        <Widget txt={this.state.txt} update={this.updateTxt} />
                        <Widget txt={this.state.txt} update={this.updateTxt} />
                        <Widget txt={this.state.txt} update={this.updateTxt} />
                        <Widget txt={this.state.txt} update={this.updateTxt} />
                        </div>
                )
        }
    });

    var Widget = React.createClass({
        render:function(){
            return (
                        <div>
                            <input type="text" onChange={this.props.update} />
                            <h1>{this.props.txt}</h1>
                        </div>
                )
        }
    })

Things to note:

  1. There's a small bug here in this code - if you change any of the input boxes, all labels change together in one go.

Using refs to access components

To solve the above problem, we give refs to the widgets and can read the value of the slider withthis.refs.red.refs.range.getDomeNode().value. Also throw a ref="range" on the component.

var APP = React.createClass({
        getInitialState:function(){
            return {
                red:0,
                green:0,
                blue:0
            }
        },
        update:function(){
            this.setState({
                red:this.refs.red.refs.range.getDOMNode().value,
                green:this.refs.green.refs.range.getDOMNode().value,
                blue:this.refs.blue.refs.range.getDOMNode().value
            })
        },
        render:function(){
            return (
                        <div>
                            <Slider ref="red" update={this.update} />
                            <label>{this.state.red}</label>
                            <Slider ref="green" update={this.update} />
                            <label>{this.state.green}</label>
                            <Slider ref="blue" update={this.update} />
                            <label>{this.state.blue}</label>
                        </div>
                )
        }
    });

    var Slider = React.createClass({
        render:function(){
            return (
                            <div>
                                <input
                                    ref="range"
                                    type="range"
                                    min="0"
                                    max="255"
                                    onChange={this.props.update} />
                            </div>
                )
        }
    })

#Accessing Child Properties

var APP =
        React.createClass({
            render:function(){
                return (
                        <BButton>I <BHeart /> React</BButton>
                    )
            }
        });

    var BButton =
        React.createClass({
            render:function(){
                return <a className="btn btn-primary">{this.props.children}</a>
            }
        });

    var BHeart =
        React.createClass({
            render:function(){
                return <span className="glyphicon glyphicon-heart"></span>
            }
        });

Things to note:

  1. this.props.children lets you access nested components and content to a subview.

#transferPropsTo

the transferPropsTo method lets you easily push properties into your components to easily customize attributes. It's smart enough to figure out that if you already have an attribute, you have to append the attribute being passed to the one that's already there.

var APP =
  React.createClass({
    render:function(){
      return (
        <div>
          <BButton href="javascript:alert('hello')" className="btn-primary">
          <BIcon className="glyphicon-heart" /> Button</BButton>
          <BButton href="javascript:alert('hello')" className="btn-success">
          <BIcon className="glyphicon-pencil" /> Button</BButton>
          <BButton href="javascript:alert('hello')" className="btn-danger">
          <BIcon className="glyphicon-inbox" /> Button</BButton>
        </div>
      )
    }
  });

var BButton =
  React.createClass({
      render:function(){
          return this.transferPropsTo(<a className="btn">{this.props.children}</a>)
      }
  });

var BIcon =
  React.createClass({
      render:function(){
          return this.transferPropsTo(<span className="glyphicon"></span>)
      }
  });

#Component Lifecycle: Mounting Basics

Things to note:

  1. componentWillMount and componentDidMount are different in that the first render is run right between the two.
  2. componentWillUnmount is what you'd expect it to be.
<body>
<button onClick="render()">Render</button>
<button onClick="unmount()">Unmount</button>
<hr />
<div id="panel"></div>
<script type="text/jsx">
    /** @jsx React.DOM */
    var APP =
        React.createClass({
            update:function(){
                var newVal = this.props.val+1
                this.setProps({val:newVal})
            },
            componentWillMount:function(){
                console.log("here i go")
            },
            render:function(){
                console.log("hello world")
                return <button onClick={this.update}>{this.props.val}</button>
            },
            componentDidMount:function(){
                console.log("nice place you got here")
            },
            componentWillUnmount:function(){
                console.log("goodbye cruel world!")
            },
        });

        window.render = function(){
            React.renderComponent(
                <APP val={0} />,
                document.getElementById('panel'))
        }
        window.unmount = function(){
            React.unmountComponentAtNode(document.getElementById('panel'))
        }
</script>

#Component Lifecycle: Mounting Usage

var APP =
  React.createClass({
      update:function(){
          var newVal = this.props.val+1
          this.setProps({val:newVal})
      },
      componentWillMount:function(){
          this.setState({m:2});
          if(this.props.val===0){
              this.btnStyle = {'color':'red'}
          }
      },
      render:function(){
          console.log("hello world")
          return  <button
                              style={this.btnStyle}
                              onClick={this.update}>
                              {this.props.val*this.state.m}
                          </button>
      },
      componentDidMount:function(){
          this.inc = setInterval(this.update,500)
      },
      componentWillUnmount:function(){
          console.log("goodbye cruel world!")
          clearInterval(this.inc)
      },
  });

  window.render = function(){
      React.renderComponent(
          <APP val={0} />,
          document.getElementById('panel'))
  }
  window.unmount = function(){
      React.unmountComponentAtNode(document.getElementById('panel'))
  }

#Component Lifecycle: Updating

Things to note:

  1. componentWillReceiveProps
  2. shouldComponentUpdate
  3. `componentWillUpdate
  4. `componentDidUpdate
var APP =
    React.createClass({
        getInitialState:function(){
            return {increasing:false}
        },
        update:function(){
            var newVal = this.props.val+1
            this.setProps({val:newVal})
        },
        componentWillReceiveProps:function(nextProps){
            this.setState({increasing:nextProps.val>this.props.val})
        },
        shouldComponentUpdate: function(nextProps, nextState) {
          return nextProps.val % 5 ===0;
        },
        componentWillUpdate: function(nextProps, nextState) {
          console.log("nextProps ===" + JSON.stringify(nextProps))
        },
        render:function(){
            console.log(this.state.increasing)
            return  (
                            <button
                                onClick={this.update}>
                                {this.props.val}
                            </button>
                            )
        },
        componentDidUpdate: function(prevProps, prevState) {
          console.log("prevProps ===" + JSON.stringify(prevProps))
        }
    });


React.renderComponent(
    <APP val={0} />,
    document.getElementById('panel'))

#Mixins

var ReactMixin = {
  componentWillMount:function(){
      console.log("will mount")
  },
  getInitialState:function(){
      return {count:0}
  },
  incrementCount:function(){
      this.setState({count:this.state.count+1})
  }
}

var APP =
  React.createClass({
      render:function(){
          return (
                  <div>
                  <buttonComponent txt="this is the button" />
                  <inputComponent txt="this is the input" />
                  </div>
              )
      }
  });
var buttonComponent =
  React.createClass({
      mixins:[ReactMixin],
      render:function(){
          return <button
                          onClick={this.incrementCount}>
                          {this.props.txt} - {this.state.count}
                       </button>
      }
  });

var inputComponent =
  React.createClass({
      mixins:[ReactMixin],
      componentWillMount:function(){
          setInterval(this.incrementCount,1000)
      },
      render:function(){
          return <input value={this.props.txt + ' - '+ this.state.count} />
      }
  });

Composable Components

Things to note:

  1. The Slider component is changed to a single NumInput component. We define an API for it.
  2. To do so, we define propTypes. Notice the update and type props.
  3. Again, numbers in props need {}
var APP = React.createClass({
  getInitialState:function(){
      return {
          red:0
      }
  },
  update:function(){
      this.setState({
          red:this.refs.red.refs.range.getDOMNode().value
      })
  },
  render:function(){
      return (
        <div>
            <NumInput
                 ref="red"
                 min={0}
                 max={255}
                 step={0.01}
                 val={+this.state.red}
                 update={this.update}
                 label="Red"
                 type="number"
             />
        </div>
      )
  }
});

var NumInput = React.createClass({
  propTypes: {
      min:React.PropTypes.number,
      max:React.PropTypes.number,
      step:React.PropTypes.number,
      val:React.PropTypes.number,
      label:React.PropTypes.string,
      update:React.PropTypes.func.isRequired,
      type:React.PropTypes.oneOf(['number','range'])
  },
  getDefaultProps:function(){
      return {
          min:null,
          max:null,
          val:0,
          step:1,
          label:'',
          type:'range'
      }
  },
  render:function(){
      var label = this.props.label!=='' ?
          <label>{this.props.label} : {this.props.val}</label> : ''
      return (
        <div>
            <input
                ref="range"
                type={this.props.type}
                min={this.props.min}
                max={this.props.max}
                step={this.props.step}
                defaultValue={this.props.val}
                onChange={this.props.update} />
                {label}
        </div>
      )
  }
})

Dynamically generated components

var PersonRow =
    React.createClass({
        render:function(){
            return (
                    <tr key={0}>
                        <td>{this.props.data.id}</td>
                        <td>{this.props.data.fname}</td>
                        <td>{this.props.data.lname}</td>
                    </tr>
                )
        }

In the App, render stuff as:

render:function(){
      return (
                  <table>
                  {this.state.data.map(function(person,i){
                      return <PersonRow key={i} data={person} />  
                  })}
                  </table>
          )
  }

You will encounter an error if you don't pass a key or it's not unique. It has to be unique amongst siblings.

Build a JSX Live Compiler

transformer.js

/** @jsx React.DOM */
var Transformer =
  React.createClass({
    getInitialState:function(){
      return {
        input:'/** @jsx React.DOM */',
        output:'',
        err:''
      }
    },
    update:function(e){
      var code = e.target.value;
      try {
        this.setState({
          output:JSXTransformer.transform(code).code,
          err:''
        })
      }
      catch(err){
        this.setState({
          err:err.message
        })
      }

    },
    render:function(){
      return (
          <div>
            <div className="row">
              <p className="alert alert-danger">&nbsp;{this.state.err}</p>
            </div>
            <div className="row">
              <textarea className="col-sm-6 input-lg" defaultValue={this.state.input} onChange={this.update} />
              <pre className="col-sm-6 input-lg">{this.state.output}</pre>
            </div>

          </div>
        )
    }
  });

#JSX Deep Dive

/** @jsx React.DOM */
/** @jsx React.FOO */

//found in knownTags
<div></div>

//!found in knownTags
<APP></APP>


//self closing tags
<div />

//multiple nodes == returning multiple functions :(
<div></div>
<a></a>

//single node == returning single function :)
<div>
    <a></a>
</div>

//first argument == component props,functions, etc.
<div>
    <a href="#"></a>
</div>

//additional arguments == children
<div>
    <a href="#">this.props.children</a>
</div>

//React will not render unknown attributes
//to the browser without data-*
<div notrendered="x" data-rendered="x">
    <a href="#">this.props.children</a>
</div>

//interpreted
<div notrendered="x" data-rendered="x">
    <a href="#" onClick={this.update}>
    {/* this is a comment */}
    this.props.children
    </a>
</div>

//if else is no good in JSX syntax, use a ternary expression
<div notrendered="x" data-rendered="x">
    <a href="#" onClick={this.update}>this.props.children</a>
        {i>1 ? 'More than one' : 'One'}
        {i>1 && 'More than one'}
        {/* this is a comment */}
    </a>
</div>


//inline styles
var myStyle={
    backgroundColor:'#000',
    height:10 //no need for 'px'
}
<div style={myStyle} notrendered="x" data-rendered="x">
    <a href="#" onClick={this.update}>this.props.children</a>
        {i>1 ? 'More than one' : 'One'}
        {i>1 && 'More than one'}
        {/* this is a comment */}
    </a>
</div>

#React - with addons: ReactLink

You can get away with much less typing by using React 0with-addons version. This shows an example of bindings via ReactLink

<head>
    <meta charset="UTF-8">
    <title>react-with-addons ReactLink</title>
    <script src="http://fb.me/react-with-addons-0.9.0.js"></script>
  <script src="http://fb.me/JSXTransformer-0.9.0.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/jsx">
/** @jsx React.DOM */
var APP =
    React.createClass({
        mixins:[React.addons.LinkedStateMixin],
        getInitialState:function(){
            return {
                name:'',
                email:'',
                phone:''
            }
        },
        render:function(){
            return (
                    <form>
                        <div>
                            <input valueLink={this.linkState('name')} type="text" placeholder="Name" />
                            <label>*{this.state.name}*</label>
                        </div>
                        <div>
                            <input valueLink={this.linkState('email')} type="text" placeholder="Email" />
                            <label>*{this.state.email}*</label>
                        </div>
                        <div>
                            <input valueLink={this.linkState('phone')} type="text" placeholder="Phone" />
                            <label>*{this.state.phone}*</label>
                        </div>
                    </form>
                )
        }
    });
React.renderComponent(
    <APP />,
    document.getElementById('app'))
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment