React applications are split into components which share information over different ways like state and properties.
Write first component
// MyComponent.js
import React from 'react'
class MyClass extends React.Component {
render (
return(
<div></div>
)
)
}
export default MyClass
Render it on the HTML
// index.js
import React from 'react'
import { render } from 'react-dom'
import MyComponent from './MyComponent'
render(<StorePicker />, document.getElementById('main'))
$r
allows access to the react object from the console.
class
tags with className
, because class is sth different in JS{/**/}
in JSX and inside parent element.Can also be included with
import './style.css'
Processed by Webpack
add attributes to components
<Component myProp='data' myObj={JSObj} onClick={doSth}>
access variables via props
class MyComponent extends React.Component {
render(
return(
<p>{this.props.myProp}</p>
<p>{this.props.myObj}</p>
<p>{this.props.onClick}</p>
)
)
}
replace class MyClass ...
with
const MyFunction = (props) => {
return(
<p>{props.myProp}<\/p>
<p>{props.myObj}<\/p>
<p>{props.onClick}<\/p>
)
}
Don't render component directly, but router
// index.js
import { BrowserRouter, Match, Miss } from 'react-router'
const Root = () => {
return (
<BrowserRouter>
<Match exactly pattern='/' component={MyComponent} \/>
<Match pattern='/mystuff/:myarg' component={MyStuff} \/>
<Miss component={OhNo} \/>
<\/BrowserRouter>
)
}
render(<Root />, document.getElementById('main'))
(Video 18): the :myarg
will be available as this.props.params.myarg
Reference form elements as follows:
<input type='text' ref={(input)=>{ this.whateverName = input }}
now you can access it through this
.
You have to bind this
in functions other than render
See this example with event handling:
class MyClass extends React.Component {
constructor () {
super()
this.myFunction = this.myFunction.bind('this')
}
myFunction (event) {
this.nowAvailable('!!!')
event.preventDefault()
}
render () {
return (
<p onClick={this.myFunction.bind(this)}>test<\/p>
<p onClick={(e) => {this.myFunction(e)}}>test<\/p>
or
<p onClick={this.myFunction}>test<\/p> Needs constructor hack
)
}
}
Use context types:
// MyComponent.js
MyComponent.contextTypes = {
router: React.PropTypes.object
}
This gives access to these functions:
blockTransitions
createHref
replaceWith
transitionTo
Usage:
this.context.router.transitionTo('...')
Initialize with
class App extends React.Component {
constructor () {
super()
this.state = {
child1state = {},
child2state = {},
}
}
render (
return (
...
)
)
}
When changing the state, you must avoid race conditions. Follow this scheme:
set the state with copy
class App extends React.Component {
constructor () {
super()
this.addChild1StateElement = this.addChild1StateElement.bind(this)
}
addChild1StateElement (elem) {
const child1state = { ...this.state.child1state }
child1state['new-item'] = elem
this.setState({ child1state })
}
render (
<ChildComponent addElem={this.addChild1StateElement} \/>
)
}
Create state manipulating functions on the main component and pass it to the children through props.
Example for
...
render () {
return (
<ul>
{Object.keys(this.state.child1state).map(key =>
<ChildComponent key={key} details={this.state.child1state[key]}\/>
)}
<\/ul>
)
}
...
each element in a loop needs a key
attribute, so React can keep track!
if you pass down key
itself, use a different name, e.g. index
!
use data massaging to extract variables for easier access
const { details } = this.props
key
itself, use a different name, e.g. index
!Object.keys(...).reduce((prevValue, key)=>{...}, initValue)
to make a scalar of an object.componentWillMount()
to sync the state before the component loads.componentWillUnmount()
to stop syncing with the database.use componentWillUpdate(nextProps, nextState)
in this case, because it is called every time state or props is updated.
...
componentWillMount () {
const localStorageRef = localStorage.getItem('mykey')
if (localStorage) {
this.setState({ order: JSON.parse(localStorageRef) })
}
}
componentWillUpdate () {
localStorage.setItem('mykey', JSON.stringify(nextState.mykey))
}
...
use shouldComponentUpdate()
to check data before it is rendered. Returns true if component should be re-rendered or false if not.
You can used computed properties
...
handleChange (event, key) {
const prev = this.props.prevs[key]
const next = { ...prev, [event.target.name]: event.target.value }
this.setState(next)
}
render () {
return (
<input type='text' name='name' value={thing.name} placeholder='Thing name' onChange={(event) => this.handleChange(event, key)} \/>
)
}
...
child1state[key] = null
is required by Firebase. Otherwise, delete child1state[key]
is also possible
...
deleteElement (key) {
const child1state = { ...this.statte.child1state }
child1state[key] = null
this.setState({ child1state })
}
...
You can store JSX in a variable.
replace parent element of animation
e.g. <ul className='myclass'>
becomes <CSSTransitionGroup className='myclass' component='ul' transitionName⁼'mytrans', transitionEnterTimeout={100} transitionLeaveTimeout={200}>
.mytrans
background blue
max-height 0px
transition all 0.5s
transform translateX(-120%)
...
&.order-enter-active
background yellow
max-height auto
transform translateX(0)
The time in transitionEnterTimeout should match the transition all <>s
There are three actions:
When sharing components with other people, use PropTypes to declared what needs to be passed to them.
const Header = function() {...}
Header.propTypes = {
tagline: React.PropTypes.string.isRequired
}
npm build
build
folder.<BrowserRouter basename='/somefolder'>
or not.in .htaccess define
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]