-
JSX adds XML syntax to JS
-
JSX tags have a tag name, attributes, and children (similar to XML)
-
Similar to JS, quotes in JSX attributes represent strings. Numeric values and/or expressions should be wrapped in a curly brace.
-
E.g
<div className =“red”>Children Text</div>; <myCounter count={3 + 5} />; var gameScores = { player1 : 2, player2 : 5 } ; <DashboardUnit data-index=“2”> <h1>Scores</h1> <Scoreboard className=“results” scores={gameScores} /> </DashboardUnit>;
-
<myCounter>
has an attribute called count that takes a numeric expression as its value -
gameScores
is an object literal that has two prop-value pairs -
<DashboardUnit>
is the block of XML that gets rendered on the page -
scores={gameScores}
: the scores attribute gets a value from the gameScores object that was defined earlier
-
You should use DOM property names like className and hmtlFor instead
-
Basic React structure :
let text = “lorem ipsum……”; reactDOM.render( <div> <a href=“#” className=“button”>Button</a> <div>{text}</div> </div>, document.getElementById(‘dom-node’) );
-
text
is a variable that currently holds some lorem ipsum text -
reactDOM.render( )
: This function is used to declare the block of code that is used to render a React component into the DOM -
{text}
: To render an expression you always need the curly braces -
document.getElementById(‘dom-node’)
: Selects the DOM node that the component interacts with -
The block of code above should render as an anchor tag (or button) with some text right below it
-
HTML tags vs. React Components :
-
To render an HTML tag use lower-case tag names (like usual) E.g
const myDiv = <div className=“foo” />; ReactDOM.render( myDiv, document.getElementById(‘dom-node’) );
-
a
<div>
is stored in aconst
calledmyDiv
and then rendered using theReactDOM.render()
function -
The function takes two args, the
<div>
stored in myDiv anddocument.getElementById()
method which is used to select the node in the DOM tree that React interacts with -
To render a React Component you need to declare a local variable in Pascal case E.g
const MyComponent = React.createClass( {/*….*/} ); const myElement = <MyComponent someProperty={true} />; ReactDOM.render( myElement, document.getElementById(‘dom-node’) ) ;
-
The
React.createClass()
function is initiated as a value forMyComponent
& this enablesMyComponent
to be recognized as a React component class. -
After the React component has been created it is then passed as a value to a
const
called myElement. A property has also been defined and the value has been set to a boolean (someProperty={true}
) -
The
ReactDOM.render()
function then takes two args; the React component that was stored inmyElement
anddocument.getElementById()
method which selects the specific DOM node that React interacts with
-
React uses the virtual DOM to clone a node that already exists in the DOM
-
Subtrees are created in the virtual DOM and then rendered based on state changes
-
When a state change occurs two things happen:
-
React runs a diff to check what changed
-
Then it updates the DOM based on the result of that diff (Referred to as reconciliation)
-
-
The Virtual DOM is considered the magic behind React. It batches DOM operations to keep everything quick and snappy because DOM manipulation is SLOW
-
Once a state is changed it triggers the diff algorithm to check all components, re-rendering only those that have changed properties.
-
When naming the Component.jsx file always make sure to use [PascalCase]((http://c2.com/cgi/wiki?PascalCase) E.g : Hello.jsx, World.jsx or HelloWorld.jsx
-
Don’t render code within the Component.jsx file. For instance this should be avoided:
// Hello.jsx
import React from 'react';
import ReactDOM from 'react-dom';
class Hello extends React.Component{
render(){
return <h1>Hello</h1>
}
}
ReactDOM.render(<Hello/>, document.querySelector('#hello'));
- The block above should be avoided because the Component.jsx file must be a module and it must export the code that is meant to be public. E.g
// Hello.jsx
import React from 'react';
class Hello extends React.Component{
render(){
return <h1>Hello</h1>
}
}
export default Hello;
- By doing this you allow the modular component to be included via
import
e.g
// index.js
import Hello from ‘./Components/Hello/Hello.jsx’; // <Hello />
import WhateverDefaultExists from ‘./Components/Hello/Hello.jsx’ // <WhateverDefaultExists />
-
In ES6 there are two kinds of exports: named exports (several per module) and default exports (one per module)
-
Use named exports when your module needs to export multiple variables/functions, and use a default export when your module only needs to export one variable/function
-
In a nutshell,
./webpack.config.js
will define the entry file(s) (index.js), and the entry file executes code while all other files simply export modules -
This applies to ALL JS modules not just React Components
- Each component has a Component.css file that contains local scoped styles (specific to that component). E.g
-Components
-Hello
-Hello.css
-Hello.jsx
- To scope the styles to the Hello component we need to structure Hello.jsx similar to :
import React from 'react';
import CSSModules from 'react-css-modules';
import styles from './Hello.css';
class Hello extends React.Component {
render() {
return <h1 className='test' styleName='test'>Hello {this.props.name}</h1>;
}
}
export default CSSModules(Hello, styles);
-
The
CSSModules
component is imported to enable the use ofstyleName
prop. In the block above the difference betweenclassName
andstyleName
is that the former is meant to be a global CSS class while the latter is meant to be CSS Modules class (locally scoped to the Hello component). -
By adopting this approach there is a clear distinction between global CSS and CSS Modules. The output looks similar to :
<h1 class="test Components-World-___World__test___1lmut">Hello</h1>
-
Each component has a set of
propTypes
that define certain attributes/properties associated with that given component -
propType
is a static method declared within a React.Component, it defines the properties and their type/requirement -
React.PropTypes
is the object that defines the validators
// Component.jsx
static propTypes = {
myProp: PropTypes.string,
myRequiredProp: PropTypes.string.isRequired
};
-
You won't use
propTypes
orReact.Proptypes
for any other reason see : https://facebook.github.io/react/docs/reusable-components.html -
propTypes
can be declared within the Component.jsx file or in the entry file where the component is being rendered -
Defined properties can be accessed via
this.props
e.g
//Component.js
class Hello extends React.Component {
render() {
return <h1 className='test' styleName='test'>Hello {this.props.name}</h1>;
}
}
//entry.js
ReactDOM.render(<Hello name="Test" />, hello);
-
The
PropTypes
method is part of the React object and so it becomes available once React has been imported into a component -
It is considered best practice to define properties specific to a component with the respectful React.PropTypes validator as this helps with debugging especially when an expected prop is missing or has the wrong type
-
A common way to use
propTypes
is similar to :
//Component.jsx
class Component extends React.Component{
static propTypes = {
name: React.PropTypes.isRequired
};
static defaultProps = {
name: 'world'
};
render() {
return <div>hello {this.props.name}</div>
}
}
-
What this does is to tell React that
name
is aPropType
that needs to have a value in order to render the component (isRequired
). -
If there is no value passed it will return an error in your console
-
To prevent syntax repetition when using
PropTypes
you can destructure thePropTypes
method from theReact
object in order to use it without reference -
This can be done when you
import
React into the Component.jsx file E.g
import React, { PropTypes } from 'react';
- Now
PropTypes
can be accessed without referencing theReact
object E.g
static propTypes = {
title: PropTypes.isRequired
};
- As opposed to :
static propTypes = {
title: React.PropTypes.isRequired
};
- As mentioned earlier, you can use the
this
keyword to access properties of a component. Using thetitle
example above the code may look like this :
<h3 styleName='title'>{this.props.title}</h3>
- To prevent
this
syntax repetition we can use destructuring again to extractthis.props
from the expression E.g
...
render(){
const { title, children, isOpen } = this.props;
return <h3 styleName='title'>{title}</h3>;
}
- What happened is that we have declared some constants (
const
) associated with our component and these constants also happen to be props that we defined earlier. So we can extract theprops
from the expressionthis.props
so that they can be used without reference. They are still associated with the component.
class FancyDiv extends React.Component {
render() {
const { foo, ...otherProps } = { foo: 'foo', bar: 'bar', 'xyz': 111 };
foo; // returns 'foo'
otherProps; // { bar: 'bar', 'xyz': 111 }
otherProps.foo // undefined
otherPops.bar; // 'bar'
otherPops.xyz; // 111
const foo = 'foo';
const bar = 'bar';
const myObject = { foo, notBar: bar }; // { foo: 'foo', notBar: 'bar' }
const { className, children, ...props } = this.props;
const actualClassName = 'goodbye ' + className;
return <div className={actualClassName} {...props}>{children}</div>;
}
}
ReactDOM.render(<FancyDiv className="hello">Hello!</FancyDiv>, dom-node);
-
We have defined a constant (
const
) and assigned an object as its value -
Inside that
const
there is a variable that looks like this :...otherProps
. It is called a spread operator -
The spread operator is used to extract values from the object that was assigned, and since
foo
has been destructured it is not extracted into the spread operator. -
So
otherProps.foo
returnsundefined
-
While
otherProps
returns{ bar: 'bar', 'xyz': 111 }
-
You can access a key in the object using
otherProps
like so :
otherProps.bar; //returns 'bar'
otherProps.xyz; //returns 111
- Let's see how this may be used with a component:
const { className, children, ...props } = this.props;
const actualClassName = 'goodbye ' + className;
return <div className={actualClassName} {...props}>{children}</div>;
ReactDOM.render(<FancyDiv className="hello">Hello!</FancyDiv>, accordion);
-
In the code above we destructure
className
,children
,...props
from the expression (this.props
) -
We then define a new constant (
const
) and assign it a string (goodbye
) and a variable that was extracted earlier (className
) -
When the next line runs, the
<div>
is rendered to the page, we assign that new constant as theclassName
and then pass along the variable...props
as an attribute to the same<div>
-
Since there is already a
className
variable that was destructured in our original set of constants we have access to it throughthis.props
. -
So the code that gets run will look similar to :
this.props.className
, thereby referencing another className on the<div>
and this makes it possible to have those two class names associated with the same<div>
E.g
<div class="goodbye hello">
- The
hello
is passed as a value to the 'className' attribute when the<div>
is rendered
Let's update the example above to accept properties.
- Import
React.PropTypes
for prop validation - Add
static propTypes
to define available props - Add
static defaultProps
to define default values
import React, { Component, PropTypes } from 'react';
class MyComponent extends Component {
static propTypes = {
name: PropTypes.string.isRequired
};
static defaultProps = {
name: 'World'
};
render() {
return <div>Hello {this.props.name}!</div>;
}
}
export default MyComponent;
import React from 'react';
import ReactDOM from 'react-dom';
import MyComponent from './MyComponent.js';
const el = document.getElementById('example');
// <div>Hello World!</div>
ReactDOM.render(<MyComponent />, el);
// <div>Hello Friend!</div>
ReactDOM.render(<MyComponent name="Friend" />, el);
A common pattern when building React components is to extend a native DOM element, allowing any standard attribute to be passed down.
In the example above, we may want to set className
or style
, however these attributes will be ignored as render
does not pass these properties to the <div>
being returned.
The code can be re-written to use object destruction to extract our custom defined props and pass through all other properties.
class MyComponent extends Component {
render() {
const { name, ...props} = this.props;
return <div {...props}>Hello {name}!</div>;
}
}
Now any extra props will be passed down to the <div>
being rendered...
// <div class="myClass">Hello World!</div>
ReactDOM.render(<MyComponent className="myClass" />, el);
// <div style="background: lime;">Hello World!</div>
ReactDOM.render(<MyComponent style={{ background: 'lime' }} />, el);
The DOM is a nested tree of elements. Separate your UI into multiple components to handle their own rendering.
class ListComponent extends Component {
static propTypes = {
children: PropTypes.node.isRequired
};
render() {
const { children } = this.props;
return <ul>{children}</ul>;
}
}
export default ListComponent;
class ItemComponent extends Component {
static propTypes = {
name: PropTypes.string.isRequired
};
render() {
const { name } = this.props;
return <li>{name}</li>;
}
}
export default ItemComponent ;
// <ul>
// <li>Item #1</li>
// <li>Item #2</li>
// <li>Item #3</li>
// </ul>
ReactDOM.render(<ListComponent>
<ItemComponent name="Item #1" />
<ItemComponent name="Item #2" />
<ItemComponent name="Item #3" />
<ListComponent>, el);
When creating dynamic children, the key
prop must be specified to uniquely identify each child node. For example, the above ListComponent
can written without the need for ItemComponent
.
class ListComponent extends Component {
static propTypes = {
items: PropTypes.array.isRequired
};
render() {
const { items } = this.props;
return <ul>{items.map((item, i) => {
return <li key={i}>{item}</li>;
})}</ul>;
}
}
const items = ['Item #1', 'Item #2', 'Item #3'];
ReactDOM.render(<ListComponent items={items} />, el);
Here is an example of a component that requires a callback property onButtonClick
to handle the onClick
event. Key notes:
onButtonClick
property added topropTypes
and default handler added todefaultProps
handleClick
method is used as a wrapper (we could have just passedonClick
prop directly)constructor
must bindthis
to thehandleClick
event handler method for ES6 classesrender
extractsonButtonClick
from props to prevent passing down to<button>
class ButtonComponent extends Component {
static propTypes = {
text: PropTypes.string.isRequired,
onButtonClick: PropTypes.func.isRequired
};
static defaultProps = {
type: 'button',
text: 'Click Me!',
onButtonClick() {
alert('Button Clicked!');
}
};
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(e) {
e.preventDefault();
this.props.onButtonClick();
}
render() {
const { text, onButtonClick, ...props } = this.props;
return <button onClick={this.handleClick} {...props}>{text}</button>;
}
}
// <button type="button">Click Me!</button>
ReactDOM.render(<Button>, el);
// Pass onButtonClick prop
const onButtonClick= () => alert('Clicked Me!');
ReactDOM.render(<Button handleClick={handleClick}>, el);
More than often you'll find yourself creating components that set a className
on their rendered DOM element.
class ButtonComponent extends Component {
render() {
return <button className="btn" {...this.props} />;
}
}
This is fine and dandy:
<ButtonComponent /> === <button className="button" />
Until you want to specify an additional className
prop...
<ButtonComponent className="blue" /> === <button className="button" />
<ButtonComponent className="blue" /> !== <button className="button blue" />
The component must be updated to handle className
as intended. In this example, we intend to concatenate both class names together:
class ButtonComponent extends Component {
static propTypes = {
className: PropTypes.string.isRequired
};
static defaultProps = {
className: '' // If we did not provide a default value, render would need to check if undefined
};
render() {
const { className, ...props } = this.props;
const buttonClasses = 'btn ' + className;
return <button className="btn" {...props} />;
}
}
This pattern can be applied to any prop, className
is just used as an example here.
a simple utility for conditionally joining classNames together.
Let's extend on the example above...
- Add
isClicked
prop to toggle the className "is-active" - Let's use classnames as it will help us here.
import classNames from 'classnames'; // import module
class ButtonComponent extends Component {
static propTypes = {
className: PropTypes.string, // removed "isRequired" as classnames will handle undefined values
isClicked: PropTypes.bool
};
static defaultProps = {
isClicked: false // this line is optional, classnames will handle undefined values
};
render() {
const { className, isClicked, ...props } = this.props;
const classList = classNames('button', className, { 'is-active': isClicked });
return <span className={classList} {...props} />;
}
}
Now we have a configurable button...
<ButtonComponent /> === <button className="button" />
<ButtonComponent isClicked={true} /> === <button className="button is-active" />
<ButtonComponent className="red" /> === <button className="button red" />
<ButtonComponent className="blink" isClicked={true} /> === <button className="button is-active blink" />
CSS Modules are awesome. If you are not familiar with CSS Modules, it is a concept of using a module bundler such as webpack to load CSS scoped to a particular document. CSS module loader will generate a unique name for a each CSS class at the time of loading the CSS document (Interoperable CSS to be precise). To see CSS Modules in practice, webpack-demo.
In the context of React, CSS Modules look like this:
import React from 'react'; import styles from './table.css'; export default class Table extends React.Component { render () { return <div className={styles.table}> <div className={styles.row}> <div className={styles.cell}>A0</div> <div className={styles.cell}>B0</div> </div> </div>; } }Rendering the component will produce a markup similar to:
<div class="table__table___32osj"> <div class="table__row___2w27N"> <div class="table__cell___2w27N">A0</div> <div class="table__cell___1oVw5">B0</div> </div> </div>and a corresponding CSS file that matches those CSS classes.
Source: https://www.npmjs.com/package/react-css-modules#css-modules
The style
property accepts an object that maps to CSS styles. This will output inline style attribute on the generated DOM element.
class MyComponent {
render() {
const styles = { backgroundColor: 'black', color: 'lime' };
return <div style={styles}>Hello World!</div>;
}
}
Styles can be dynamic based on the component's current properties.
class MyComponent {
static defaultProps = {
bgColor: 'black',
txtColor: 'lime'
};
render() {
const { bgColor, txtColor } = this.props;
const styles = { backgroundColor: bgColor, color: txtColor };
return <div style={styles}>Hello World!</div>;
}
}
Here is another example..
class MyComponent {
static defaultProps = {
isBig: false
};
render() {
const { isBig} = this.props;
const styles = isBig ? { fontSize: '300%' } : null;
return <div style={styles}>Hello World!</div>;
}
}
- When working with an SVG sprite file it is important to have the namespace declaration in the file E.g
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="sprite">
- The SVG may not get rendered without the namespace in the opening tag
-
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
-
https://github.com/ModuleLoader/es6-module-loader/wiki/Brief-Overview-of-ES6-Module-syntax
-
https://facebook.github.io/react/docs/thinking-in-react.html
-
https://facebook.github.io/react/docs/reusable-components.html