-
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 -
gameScoresis 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’) ); -
textis 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 aconstcalledmyDivand 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 enablesMyComponentto be recognized as a React component class. -
After the React component has been created it is then passed as a value to a
constcalled 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 inmyElementanddocument.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
importe.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.jswill 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
CSSModulescomponent is imported to enable the use ofstyleNameprop. In the block above the difference betweenclassNameandstyleNameis 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
propTypesthat define certain attributes/properties associated with that given component -
propTypeis a static method declared within a React.Component, it defines the properties and their type/requirement -
React.PropTypesis the object that defines the validators
// Component.jsx
static propTypes = {
myProp: PropTypes.string,
myRequiredProp: PropTypes.string.isRequired
};-
You won't use
propTypesorReact.Proptypesfor any other reason see : https://facebook.github.io/react/docs/reusable-components.html -
propTypescan 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.propse.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
PropTypesmethod 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
propTypesis 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
nameis aPropTypethat 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
PropTypesyou can destructure thePropTypesmethod from theReactobject in order to use it without reference -
This can be done when you
importReact into the Component.jsx file E.g
import React, { PropTypes } from 'react';
- Now
PropTypescan be accessed without referencing theReactobject E.g
static propTypes = {
title: PropTypes.isRequired
};- As opposed to :
static propTypes = {
title: React.PropTypes.isRequired
}; - As mentioned earlier, you can use the
thiskeyword to access properties of a component. Using thetitleexample above the code may look like this :
<h3 styleName='title'>{this.props.title}</h3> - To prevent
thissyntax repetition we can use destructuring again to extractthis.propsfrom 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 thepropsfrom the expressionthis.propsso 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
constthere 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
foohas been destructured it is not extracted into the spread operator. -
So
otherProps.fooreturnsundefined -
While
otherPropsreturns{ bar: 'bar', 'xyz': 111 } -
You can access a key in the object using
otherPropslike 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,...propsfrom 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 theclassNameand then pass along the variable...propsas an attribute to the same<div> -
Since there is already a
classNamevariable 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
hellois passed as a value to the 'className' attribute when the<div>is rendered
Let's update the example above to accept properties.
- Import
React.PropTypesfor prop validation - Add
static propTypesto define available props - Add
static defaultPropsto 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:
onButtonClickproperty added topropTypesand default handler added todefaultPropshandleClickmethod is used as a wrapper (we could have just passedonClickprop directly)constructormust bindthisto thehandleClickevent handler method for ES6 classesrenderextractsonButtonClickfrom 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
isClickedprop 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