#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:
- Number props need to be sent in {}, strings do not
- Way to specify what's expected and/or required
- Way to specify default props
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:
- How to assign initial state
- How to update state
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:
- There's a small bug here in this code - if you change any of the input boxes, all labels change together in one go.
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:
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:
componentWillMount
andcomponentDidMount
are different in that the first render is run right between the two.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:
componentWillReceiveProps
shouldComponentUpdate
- `componentWillUpdate
- `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} />
}
});
Things to note:
- The Slider component is changed to a single NumInput component. We define an API for it.
- To do so, we define
propTypes
. Notice theupdate
andtype
props. - 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>
)
}
})
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.
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"> {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>