- General React principles
- Setup
- Importing react
- Functional components
- Class components
- PropType (property validation)
- Styling your components
- Managing state
- Composing components
- "Lifting state"
- Component lifecycle
- Improve performance
- Accessing the regular DOM, using
refs
- Further reading
In the typical React dataflow, props are the only way that parent components interact with their children.
Components should be "pure" - they should not modify their inputs.
A component must never modify its own props.
To modify a child, you re-render it with new props.
Any data shared by components should live in the state of the parent component.
npm install @babel/core babel-loader @babel/preset-env @babel/preset-react react-dom react prop-types
In .babelrc
:
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
Webpack config:
module.exports = {
//...
module: {
rules: [
{
- test: /\.js$/,
+ test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: 'babel-loader'
}
]
},
resolve: {
extensions: [
'.js',
+ '.jsx'
]
}
// ...
}
Generally:
import React from "react";
import ReactDOM from "react-dom";
Specific packages:
import React, { Component } from "react";
import ReactDOM from "react-dom";
or
import { useState } from "react";
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
class MyComponent extends React.Component {
// Set state as a class field (preferred method)
// This requires Babel option `@babel/plugin-proposal-class-properties`
this.state = {
foo: 'initialValue',
bar: this.props.bar, // 'props' is passed into the constructor (see below)
}
constructor(props) { // fires before component is mounted
// makes 'this' refer to this component, not React.Component
super(props);
// Set state (alternative method the using class fields, above)
this.state = {
foo: 'initialValue',
bar: this.props.bar,
}
// class methods should normally be bound to 'this'
this.fooExists = this.fooExists.bind(this);
}
fooExists() {
return !!this.state.foo;
}
render() {
/*
* 1. Components must return 1 (container) element only
* 2. Encase multiple return lines in parentheses
* 3. In JSX, HTML attributes are in camelCase
* 4. You can not use `setState()` within a render function!
* 5. Use quotes when your props are strings: `foo="someString"`
* 6. Use braces when your props are JavaScript: `foo={someJS}`
*
*/
return (
<div>
<h1>Hello {this.state.foo}</h1>
<input
className='someClass'
type='text'
value={this.state.bar}
/>
</div>
)
}
}
Enable validation of component 'props' (requires prop-types
).
import React from 'react';
+import PropTypes from "prop-types";
class MyComponent extends React.Component {
// ...
}
+ MyComponent.propTypes = {
+
+ // required
+ foo: PropTypes.string.isRequired,
+
+ // optional
+ bar: PropTypes.string,
+
+ // an object of a particular shape
+ someObject: PropTypes.shape({
+ colour: PropTypes.string,
+ fontSize: PropTypes.number
+ }),
+
+ };
+
export default MyComponent;
Simply import a css file. You should have a separate css file for each component:
Files:
./MyComponent/
./MyComponent/MyComponent.css
./MyComponent/MyComponent.jsx
In MyComponent.css
:
.msc-container {
...;
}
.msc-para {
...;
}
In MyComponent.jsx
import React from "react";
import "./MyComponent.css";
const MyComponent = () => (
<div className="msc-container">
<p className="msc-para">Get started with CSS styling</p>
</div>
);
export default MyComponent;
Create a style object, then add a style property:
const divStyle = {
color: 'blue',
backgroundImage: 'url(' + imgUrl + ')',
};
class MyComponent extends React.Component {
...
render() {
return (
<div style={divStyle}>
<p>Hello</p>
</div>
);
}
Or add the style object inline (without defining it earlier):
<div style={divStyle}>
- <p>Hello</p>
+ <p style={{ fontSize: '12px' }}>Hello</p>
</div>
- Your state is encapsulated: It is only accessible to the component that owns and sets it.
- However, a component can pass its state down as props to its child components
- Components re-render each time receives new props
Let's add a ResultPanel
component to some other one:
render(){
return !!this.state.gssid
? <ResultPanel gssid={this.state.gssid} />
: '<p>Choose a place</p>';
}
The ResultPanel
component would receive the gssid in its props
:
function ResultPanel(props) {
return (
<div>
<h2>Location:</h2>
<p>{props.gssid}</p>
</div>
);
}
To ensure that you do stuff only after it setState()
has finished updating
the component state, use an anonymous callback, and do stuff in there:
someFunc = () => {
// pass an anonymous functions as a 2nd param to `setState`
// and do stuff in the anonymous func, to ensure
// you do stuff only _after_ state was updated
this.setState({ foo: this.state.foo + 1 }, () => {
// check this.state.foo here,
// as we know it was updated
if (this.state.foo > 7) {
this.setState({ bar: 20 });
}
});
};
Never modify this.state
directly, instead use:
this.setState({ foo: 'bar' });
, orthis.setState({ this.state..., foo: 'bar' });
..how to add components together, to make a larger complex component:
// define a re-usable component (re-usable by passing in 'props')
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Tom" />
<Welcome name="Dick" />
<Welcome name="Sally" />
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
For when you need two components to be in sync with each other (share the same data)..
In React, sharing state is accomplished by moving it up to the closest common ancestor of the components that need it.
First, update the children components:
- remove setting of state values
- add passing in of state as
props
to the render methods
class ChildOne extends React.Component {
contructor(props) {
super(props);
- this.state.foo = 'ONE';
}
- updateFoo() {
- this.setState({this.state..., foo});
- }
// ...
- render() {
+ render(props) {
- return <p>{this.state.foo}</p>
+ return <p>{this.props.foo}</p>
}
}
class ChildTwo extends React.Component {
contructor(props) {
super(props);
- this.state.foo = 'TWO';
}
- updateFoo(foo) {
- this.setState({this.state..., foo});
- }
// ...
- render() {
+ render(props) {
- return <p>{this.state.foo}</p>
+ return <p>{this.props.foo}</p>
}
}
Then update the parent component:
- move state handling methods to this component
- pass the state into the children components as
props
class MainComponent extends React.Component {
constructor(props) {
super(props);
this.state = { foo: 'bar' };
// class methods need to be bound to 'this'
this.updateFoo = this.updateFoo.bind(this);
}
+ updateFoo(foo) {
+ this.setState({this.state..., foo});
+ }
// ...
render() {
return (
- <ChildOne />
- <ChildTwo />
+ <ChildOne foo={this.state.foo} />
+ <ChildTwo foo={this.state.foo} />
)
}
}
In applications with many components, it’s very important to free up resources taken by the components when they are destroyed.
Example: unsetting event handlers, killing timers, etc.
You should do these things in the right part of the component "lifecycle".
Here are the lifecycle methods of a React component:
class myComponent extends React.Component {
constructor() {
// fires once on component init
}
componentWillMount() {
// fires immediately before the initial render
}
componentDidMount() {
// fires immediately after the initial render
//
// Most common use case for componentDidMount:
// start AJAX calls to load in data
//
// You can also setup up event handlers, timers, etc here
//
// You *could* modify or set the state here, but
// it's not recommended - the initial render will the
// update, so set it in the constructor instead.
}
componentWillReceiveProps() {
// fires when component is receiving new props
}
shouldComponentUpdate() {
// fires before rendering with new props or state
}
componentWillUpdate() {
// fires immediately before rendering
// with new props or state
}
componentDidUpdate() {
// fires immediately after rendering with new P or S
}
componentWillUnmount() {
// fires immediately before component is unmounted
// from DOM (removed)..
//
// This is a good place for clearInterval(foo),
// removeEventListener, window.cancelAnimationFrame, etc
}
render() {
// fires when props or state is updated
}
}
By default React will re-render the component each time its props
change.
Here's the default behavior of a component, deciding when to update (re-render):
shouldComponentUpdate(nextProps, nextState) {
return true;
}
If you know of any cases where you your component does not need to re-render,
then you should return false
in shouldcomponentUpdate
in those cases.
Example: only re-render the component when props.colour
changes:
shouldComponentUpdate(nextProps, nextState) {
if (this.props.colour !== nextProps.colour) {
return true;
}
return false;
}
Keep in mind that it can cause major problems if you set it and forget it, because your React component will not update normally. So use with caution.
For accessing regular DOM elements, using the regular DOM API, use refs
.
When you might need refs:
- you might need to set focus on a
<input type="text">
element - you might need to integrate a non-React, third party library
Creating a ref
:
class myInput extends React.Component {
constructor(props) {
super(props);
this.state = {
foo: 'initialValue',
bar: this.props.bar,
}
+ this.inputElemref = React.createRef();
}
...
render() {
return (
<input
className='someClass'
type='text'
+ ref={this.inputElemRef}
value={this.state.bar}
/>
)
}
}
Accessing the ref
:
focusInput() {
// You must access "current" to get the DOM node using the **raw DOM API**
this.inputElemRef.current.focus();
}
- Components and Props
- 5 ways to style react components in 2019
- React CSS-in-JS comparison
- State and Lifecycle
- Lifting State up
- Refs and the DOM
- react-16-lifecycle-methods-how-and-when-to-use-them
- React lifecycle-simulators
- PropTypes
- Common React problems
- The 7 most common mistakes that react developers make