...in progress...
Basics of ReactJs and recommended VS Code extensions.
- React tutorial: https://reactjs.org/docs/getting-started.html
- W3 Schools React tutorial: https://www.w3schools.com/react/default.asp
- VS Code Extensions: https://github.com/stesvis/ReactJs-Cheat-Sheet/blob/master/vscode-extensions.md
- React Developer Tools: https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en
- Installation
- Components
- State
- Events
- Component Lifecycle
- Lists
- Forms
- CSS
- Consuming APIs
- Routing
- Hooks
- Context
- Other Topics
- Install NodeJs: https://nodejs.org/en/
- Create the app via command line:
npx create-react-app <app_name>
Then:
cd <app_name>
npm start
- Takes
props
as an argument
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
- Accepts
props
as an argument in the constructor - Can access props with
this.props
class Welcome extends React.Component {
constructor(props) {
super(props);
}
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
- State can only be used in class components
- State must be initialized in the constructor before you can use it
constructor(props) {
super(props);
this.state = { name: 'John' };
}
- State can not be modified directly
this.state.name = 'Michael'; // WRONG
this.setState({ name: 'John' }; // correct
React has the same events as HTML: click, change, mouseover etc. React events are written in camelCase syntax:
onClick
instead ofonclick
.
React event handlers are written inside curly braces:
onClick={handleClick}
instead ofonClick="handleClick()"
.
To be able to use this
in an event handler you have to use arrow functions:
handleClick = () => {
// you can use this
console.log(this);
}
To pass arguments you have to use the arrow function when you define the event:
onClick={() => this.handleClick('Goal')}
And of course use it in the handler too:
handleClick = (arg) => { ... }
Another option to use this
in an event handler is to bind it in the constructor:
constructor(props) {
super(props);
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
You can pass the actual event and it will be automatically available:
handleClick = (event) => {
console.log(event);
}
onClick={this.handleClick}
class Football extends React.Component {
handleClick = (arg, event) => {
console.log(arg); // 'Goal'
}
render() {
// event is optional, but you can pass it like this:
return (
<button onClick={(event) => this.handleClick('Goal', event)}>Take the shot!</button>
);
}
}
The three phases are: Mounting, Updating, Unmounting and ErrorHandling.
constructor(props) { ... }
: set the state, call APIs etc
constructor(props) {
super(props);
this.state = {favoritecolor: "red"};
}
static getDerivedStateFromProps(props, state) { ... }
: this is the natural place to set thestate
object based on the initialprops
static getDerivedStateFromProps(props, state) {
return {favoritecolor: props.favcol };
}
render() { ... }
: outputs HTML to the DOMcomponentDidMount() { ... }
: called after the component is rendered
static getDerivedStateFromProps(props, state) { ... }
: the first method that is called when a component gets updated. This is still the natural place to set the state object based on the initial propsshouldComponentUpdate(nextProps, nextState) { ... }
: return a Boolean value that specifies whether React should continue with the rendering or notrender() { ... }
getSnapshotBeforeUpdate(prevProps, prevState) { ... }
: you have access to theprops
andstate
before the update, meaning that even after the update, you can check what the values were before the update. If thegetSnapshotBeforeUpdate()
method is present, you should also include thecomponentDidUpdate()
method, otherwise you will get an error.
getSnapshotBeforeUpdate(prevProps, prevState) {
// Are we adding new items to the list?
// Capture the scroll position so we can adjust scroll later.
if (prevProps.list.length < this.props.list.length) {
const list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) { ... }
: called after the component is updated in the DOM
componentDidUpdate(prevProps, prevState, snapshot) {
// If we have a snapshot value, we've just added new items.
// Adjust scroll so these new items don't push the old ones out of view.
// (snapshot here is the value returned from getSnapshotBeforeUpdate)
if (snapshot !== null) {
const list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}
componentWillUnmount() { ... }
: called when the component is about to be removed from the DOM
static getDerivedStateFromError(error)
: returns a new state or null
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, info)
: called during the “commit” phase, so side-effects are permitted. It should be used for things like logging errors
componentDidCatch(error, info) {
// Example "componentStack":
// in ComponentThatThrows (created by App)
// in ErrorBoundary (created by App)
// in div (created by App)
// in App
logComponentStackToMyService(info.componentStack);
}
To render lists keep in mind two things:
- You should use the javascript
map()
function - Every list item must have a
key
const persons = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Mary' },
{ id: 3, name: 'Michael' },
];
const listItems = persons.map((person) =>
<li key={person.id}>
{person.name}
</li>
);
React forms tutorial: https://reactjs.org/docs/forms.html W3 Schools React forms tutorial: https://www.w3schools.com/react/react_forms.asp
Formik: https://formik.org/docs/overview
- The
state
field names must match theform
field names - You can make a
handleChange
event handler to handle changes from eachform
field - The
handleSubmit
event handler needs to callevent.preventDefault()
to prevent page reload, check the form validation, and submit the form via API call
class MyForm extends React.Component {
constructor(props) {
super(props);
// state field names must match the form field names
this.state = {
username: 'Initial value',
age: null,
isFriendly: false,
gender: null,
myCar: 'Volvo',
errorMessage: ''
};
}
handleSubmit = (event) => {
event.preventDefault(); // avoids page reload
let age = this.state.age;
// you can validate on submit
if (!Number(age)) {
//alert("Your age must be a number");
error = <strong>Your age must be a number</strong>;
this.setState({ errorMessage: error });
return;
}
// call the POST api
}
handleChange = (event) => {
//let fieldName = event.target.name;
//let value = event.target.value;
let { fieldName, value, type, checked } = event.target; // extract those two values
// you can do live validation in this handler or in the submit handler
if (fieldName === "age") {
if (!Number(value)) {
//alert("Your age must be a number");
error = <strong>Your age must be a number</strong>;
}
}
this.setState({ errorMessage: error });
type === 'checkbox' ? this.setState({ [name]: checked }) : this.setState({ [fieldName]: value }); // access the field via state array
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<h1>Hello {this.state.username} {this.state.age}</h1>
{this.state.errorMessage}
<!-- Text -->
<p>Enter your name:</p>
<input
type='text'
name='username'
value='{this.state.username}'
onChange={this.handleChange}
/>
<!-- Number -->
<p>Enter your age:</p>
<input
type='text'
name='age'
onChange={this.handleChange}
/>
<!-- Checkbox -->
<label>
<input type="checkbox" name="isFriendly" checkeck={this.state.isFriendly} onChange="{this.handleChange} />
Is friendly
</label
<!-- Radio buttons -->
<label>
<input type="radio" name="gender" value="Male" checkeck={this.state.gender === 'male'} onChange="{this.handleChange} />
Male
</label>
<label>
<input type="radio" name="gender" value="Female" checkeck={this.state.gender === 'female'} onChange="{this.handleChange} />
Female
</label>
<!-- Select -->
<select value={this.state.myCar} onChange={this.handleChange}>
<option value="Ford">Ford</option>
<option value="Volvo">Volvo</option>
<option value="Fiat">Fiat</option>
</select>
<
</form>
);
}
}
You can style components using CSS, but the property names must be camelCased like for the events, for example: backgroundColor
vs background-color
<h1 style={{ backgroundColor: 'blue' }}>This is a Title</h1>
render() {
const bigBlueTitleStyle = {
color: DodgerBlue;
padding: 40px;
font-family: Arial;
text-align: center;
};
return (
<h1 style={bigBlueTitleStyle}>This is a Title</h1>
);
}
You can write your CSS styling in a separate file, just save the file with the .css
file extension, and import it in the application.
body {
background-color: #282c34;
color: white;
padding: 40px;
font-family: Arial;
text-align: center;
}
<!-- Define other styles as usual -->
import './App.css';
Create the CSS module with the .module.css
extension:
.bigBlueTitle {
color: DodgerBlue;
padding: 40px;
font-family: Arial;
text-align: center;
}
Import the stylesheet in your component and use the style:
import styles from './mystyle.module.css';
return <h1 className={styles.bigBlueTitle}>This is a Title</h1>;
You can use the javascript fetch()
or a library like Axios.
Usually you fetch data in the componentDidMount()
method:
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(data => console.log(data));
}
Axios: https://github.com/axios/axios
axios.request(config)
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.options(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
axios.patch(url[, data[, config]])
GET example:
const axios = require('axios');
// Make a request for a user with a given ID
axios.get('/user?ID=12345')
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
ALL example:
axios.all([
axios.get('https://jsonplaceholder.typicode.com/posts'),
axios.get('https://jsonplaceholder.typicode.com/users')
])
.then(response => {
console.log('Date created: ', response[0].data.created_at);
console.log('Date created: ', response[1].data.created_at);
});
You need to install react-router-dom:
$ npm install --save react-router-dom
And then you can start using components like<Router>
(BrowserRouter),<Link>
,<Switch>
and<Route>
to build navbars and handle your menus.
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
// This site has 3 pages, all of which are rendered
// dynamically in the browser (not server rendered).
//
// Although the page does not ever refresh, notice how
// React Router keeps the URL up to date as you navigate
// through the site. This preserves the browser history,
// making sure things like the back button and bookmarks
// work properly.
export default function BasicExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/dashboard">Dashboard</Link>
</li>
</ul>
<hr />
{/*
A <Switch> looks through all its children <Route>
elements and renders the first one whose path
matches the current URL. Use a <Switch> any time
you have multiple routes, but you want only one
of them to render at a time
*/}
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/dashboard">
<Dashboard />
</Route>
</Switch>
</div>
</Router>
);
}
// You can think of these components as "pages"
// in your app.
function Home() {
return (
<div>
<h2>Home</h2>
</div>
);
}
function About() {
return (
<div>
<h2>About</h2>
</div>
);
}
function Dashboard() {
return (
<div>
<h2>Dashboard</h2>
</div>
);
}
Hooks allow to use functional components with state
.
Note: Don’t call Hooks inside loops, conditions, or nested functions.
With this hook you can use state
in a functional component.
https://reactjs.org/docs/hooks-state.html
It takes an argument which is the initial value of the state property and it returns an array of two elements:
- The state property
- A function to update that property
const [<variable_name>, <function>] = useState(<variable_initial_value>);
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
The Effect Hook lets you perform side effects in function components.
https://reactjs.org/docs/hooks-effect.html
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
return () => {
// runs before every component update
};
}); // no second argument: runs at every component update
useEffect(() => {
return () => {
// componentWillUnmount()
};
}, []); // this will make it run only once, to simulate componentWillUnmount()
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
- if you do not pass a second argument it will execute
useEffect
every time the component is re-rendered (so on every change ofstate
and/orprops
- if you pass an array of
state
and/orprops
a second argument touseEffect
it will executeuseEffect
every time one of those variables are updated - if you pass an empty array it will execute
useEffect
only once, and it's the equivalent ofcomponentDidMount()
:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
}, []); // will only execute it once
memo()
is used to remember the state of a component, and should be used in functional components to simulate the shouldComponentUpdate()
event.
Basically:
- return
true
if you don't want to re-render - return
false
if you want to re-render
The second function argument is however optional.
function Example(props) {
return (
// do stuff
);
}
export default React.memo(Example, (prevProps, nextProps) => {
// in a form: component should not update if the title did not change
return prevProps.title === nextProps.title;
});
Context
allows you to pass props from a parent component directly to any child component without passing it to every component in the tree.
TODO
https://reactjs.org/docs/context.html
You should use <Fragment>...</Fragment>
to wrap the return value of a component instead of wrapping everything inside a <div>...</div>
class Columns extends Component {
render() {
return (
<Fragment>
<td>Hello</td>
<td>World</td>
</Fragment>
);
}
}
The code above results in the following html
<table>
<tr>
<td>Hello</td>
<td>World</td>
</tr>
</table>
It expands the array into individual elements. The syntax is [...<name_of_the_array_to_spread>]
.
const react = ['React'];
const spelling = [...react];
// ['R', 'e', 'a', 'c', 't']