React is made of ....
React maintains it's own hierarchy of react elements. React elements are plain javascript objects instead of DOM objects, so they are very light-weight and fast to process. React keeps the real DOM elements in sync with the react DOM. When something changes, React compares the new components with the current ones and only re-renders the components that have changed.
A React component will start with a root element that exists somewhere inside a HTML document. Everything under that element will be controlled by the React DOM.
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));
A typical React application will have a single root element. A legacy application with a mix of react and non-react components may have multiple root components.
React components are immutable. When a component changes, the old one is replaced by a new one with new values.
JSX is a syntax extension to Javascript that treats HTML tags as first class language constructs.
JSX syntax gets compiled into standard javascript that creates the HTML markup through an API that looks much like the DOM in the browser. It gets compiled into standard javascript by Babel.
JSX natively supports templating by embedding javascript code in curly braces inside the code:
const element = <h1>Hello, {name}!</h1>;
JSX supports quotes inside of mark-up:
const element = <h1 class="my-element">{name}</h1>
If you want to use a javascript expression for an attribute, wrap it in curly braces instead. You don't need both quotes and curly braces:
const element = <img src={user.profile} alt={user.name} />
It's important to wrap JSX in brackets if you want to spread it across multiple lines to prevent extra semi-colons being inserted at the end of the lines:
const element = (
<h1>Hello, {name}!</h1>
);
Tags inside of JSX may be nested. Use XML-style empty tag syntax for tags without any children.
const element = (
<div>
<h1 class="my-element">{name}</h1>
<img src={user.profile} alt={user.name} />
</div>
);
JSX automatically escapes any values embedded into the DOM, to prevent XSS attacks. You do not need to explicitly escape the input yourself just to display it to the user in the browser.
Components allow you to break a complex UI into simple, loosely coupled pieces that can be reasoned about in isolation. They are a collection of state, React elements and javascript functionality bundled into a single Javascript class or function.
The standard type of React component is a class that extends React.Component:
class MyComponent extends React.Component {
render() {
return (
<h1 className="my-component">{this.props.title}</h1>
);
}
}
It's also possible to create a function component for simple components. It just needs to be a function that takes properties as a parameter and returns the elements to be rendered:
function MyComponent(props) {
return (
<h1 className="my-component" onClick={() => props.onClick()}>
{props.title}
</h1>
);
}
You can reference your own components in JSX interchangeably with DOM components. Elements which start with a lowercase letter are assumed to be DOM elements. Elements which start with a capital letter are assumed to be user-defined components:
const element = <Hello name="World" />;
...
function Hello(props) {
return <h1>Hello {props.name}</h1>;
}
When React sees a user-defined component, it passes any attributes to the component as a read-only parameter called props. A component should never update its props. Ideally a component with the same props should always return the same output.
Components can return other components in their output. This allows you to build up complex UIs from simple individual components.
const element = <Hello name="World" />;
...
function Hello(props) {
return (
<h1>
Hello {props.name}
<ProfileImage src={props.profileImage} />
</h1>
);
}
React components have a predictable lifecycle of methods that get called during their creation, when they get updated and when they get deleted. You can override these methods to optimise and customise your components, based on state changes and events.
The only method that a component must implement is render to return a list of JSX elements to be added to the React DOM. All other methods receive default implementations from the Component base class.
Props are pieces of public component state that can be set by javascript code outside of the component, usually by the component's parent components. State is the component's internal state.
You must not alter a component's state directly, you should always call the setState method. The only place where it is ok to set the component's state directly is the constructor. This is because setState will ultimately kick off a process that will lead to the component re-rendering if the state has changed.
Calling setState raises a request to update the state that may not be executed straight away. Multiple calls may be batched.
It is possible to pass an object to setState that will update multiple properties at once. The object will be shallowly merged with the component's current state object.
this.setState({
name: 'helen',
level: 'react-beginner',
});
This also means reading the components internal state directly might lead to getting out of date information if the setState request isn't applied straight away. It's possible to pass a callback to setState that will only be executed once the state is applied but it's recommended to put that logic into componentDidUpdate instead.
If your setState logic requires knowledge of the current state or props, you should pass setState an updater function that will receive the current state and props as parameters. The state and props parameters should not be directly mutated, instead a new state object should be returned from the function:
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
The state for each component is encapsulated inside it.
All state is owned by a particular component. State flows downward in a React DOM, from parent to child and never in the opposite direction. A parent component may decide to share its state with child components, but the child components will never share state with parent components.
This is important to create components that are isolated, resuable and simple to reason about.
Event handling is very similar to DOM event handling with these differences:
- events are camelCase (like
onClick
) - pass event handlers using curly brackets rather than quotes (like
onClick={doSomething}
)
The usual gotchas of javascript still apply:
- use function.bind() to set this to the correct value (like
this.handleClick = this.handleClick.bind(this)
) - Use closures to pass variables into event handlers
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick = () => {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<MyComponent />,
document.getElementById('root')
);
- Most React applications start with an App root component
- Logic for rendering is inherently coupled with UI logic, so the two should be kept together
- UIs are made up of small, loosely coupled components that can be reasoned about independently
- Components should always return the same output for the same props. They should never modify their props.
- Make state immutable - for all the usual reasons of simplifying flow and reducing side effects, but also because it means react knows when your component needs to re-render
- State flows downward in a React DOM, from parent to child and never in the opposite direction
- Reuse through composition rather than inheritance
There are react developer tools for Chrome and Firefox
To start a new react project, there is a create-react-app console program on NPM that will set up a scaffolding for you.
npx create-react-app my-app
- TicTacToe - following the first React tutorial