React.js
React (Facebook)
- JavaScript UI library
- not a full fledged framework because React has no router
- however, there is a package called
react-router-dom
- however, there is a package called
- not a full fledged framework because React has no router
- Maintained by Facebook
- alternatives to React:
- Angular (2016, Google, full fledged framework)
- complete rewrite of AngularJS
- TypeScript based
- Vue.js (2014, Evan You, gaining popularity, easy to learn)
- Angular (2016, Google, full fledged framework)
JSX (Facebook)
AKA JavaScript Syntax Extension, (JavaScript XML)
- looks like HTML
- React components are typically written using JSX (but can also be written in pure JavaScript)
- JSX provides a way to structure component rendering
- JSX is a syntax extension of JavaScript
- JSX was created by Facebook
- similarly, XHP is a syntax extension for PHP
- XHP was also created by Facebook
Using JavaScript inside of JSX
- wrap the code in curly braces
Tutorials
digitalocean.com
- Props (TODO)
- Class-based State Management, Components
- Hooks
- Web APIs (TODO)
- Login Authentication (TODO)
Setup for the digitalocean.com Tutorial
npx create-react-app my-app-name
# Delete the line `import logo from './logo.svg';`. Then replace everything in
# the return statement to return a set of empty tags: <></>.
nvim src/App.js
rm src/logo.svg
# Each component will have its own directory in 'components/' to store the
# component file along with the styles, images, and tests.
mkdir src/components
mkdir src/components/App
# Move all of the App files into that directory.
mv src/App.* src/components/App/
# change line to: `import App from './components/App/App';`
nvim src/index.js
Fundamentals
Elements
- legacy.reactjs.org
- React objects
- React's representation of browser DOM elements
Components
- React components
- "Conceptually, components are like JavaScript functions."
- "They accept arbitrary inputs (called "props") and return React elements describing what should appear on the screen."
Lists and Keys
From legacy.reactjs.org:
Creating a list:
Transforming arrays into lists of React elements:
- JavaScript
- JSX/React
// create an array
const numbers = [1, 2, 3, 4, 5];
// iterate over this array
const doubled = numbers.map((number) => number * 2);
// print the result
console.log(doubled);
const numbers = [1, 2, 3, 4, 5];
// Build collections of elements
const listItems = numbers.map((numbers) =>
<li>{numbers}</li>
);
const root = ReactDOM.createRoot(document.getElementById('root'));
// and include them in JSX using curly braces {}
root.render(<ul>{listItems}</ul>);
Usually you would render lists inside a React component.
Refactor the previous example into a React component that accepts an array of numbers and outputs a html unordered list of React elements:
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<NumberList numbers={numbers} />);
This will throw a warning in the Firefox Console (ctrl shift k) (it may be necessary to reload the page ctrl shift r before this is shown).
Warning: Each child in a list should have a unique "key" prop.
Check the render method of `NumberList`. See https://reactjs.org/link/warning-keys for more information.
li
NumberList@http://localhost:3001/static/js/bundle.js:24:19
Fix this warning by adding a key:
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
- "Keys should be given to the elements inside the array to give the elements a stable identity"
"When you don’t have stable IDs for rendered items, you may use the item index as a key as a last resort:"
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={index}>
{todo.text}
</li>
);
"We don't recommend using indexes for keys if the order of items may change. This can negatively impact performance and may cause issues with component state."
State Management
- state management: In React development, keeping track of how your application data changes over time is called "state management".
- common state management methods:
- class-based state management
- Hooks
- Redux (third-party library)
- React may optimize code by calling actions asynchronously
- make sure that your function has access to the most up-to-date state
- common state management methods:
setState(nextState, callback?)
From: react.dev: setState
Parameters
nextState: Either an object or a function.- If you pass an object as
nextState, it will be shallowly merged intothis.state. - If you pass a function as
nextState, it will be treated as an updater function. It must be pure, should take the pending state and props as arguments, and should return the object to be shallowly merged intothis.state. React will put your updater function in a queue and re-render your component. During the next render, React will calculate the next state by applying all of the queued updaters to the previous state.
- If you pass an object as
- optional
callback: If specified, React will call thecallbackyou’ve provided after the update is committed.- What is a callback used for? - see geeksforgeeks
Usage
From: react.dev: setState
- "Call
setStateto update the state of your React component." - "
setStateenqueues changes to the component state. It tells React that this component and its children need to re-render with the new state." - "This is the main way you’ll update the user interface in response to interactions."
- "You can also pass a function to
setState. It lets you update state based on the previous state: example"
3 things you should know about setState()
From: legacy.reactjs.org: with examples
1. Do not modify state directly
For example, this will not re-render a component:
// Wrong
this.state.comment = 'Hello';
Instead, use setState():
// Correct
this.setState({comment: 'Hello'});
- "The only place where you can assign
this.stateis the constructor."- if you assign it somewhere else, the component will not re-render
- use spread for copying arrays
2. State updates may be asynchronous
- React may batch multiple
setState()calls into a single update for performance. - Because
this.propsandthis.statemay be updated asynchronously, you should not rely on their values for calculating the next state.
For example, this code may fail to update the counter [phth: counter is like the total price in the digitalocean tutorial]:
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
To fix it, use a second form of setState() that accepts a function rather than an object. That function will receive the previous state as the first argument, and the props at the time the update is applied as the second argument:
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
We used an arrow function above, but it also works with regular functions:
// Correct
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});
3. State updates are merged
When you call setState(), React merges the object you provide into the current state.
For example, your state may contain several independent variables [here: posts and comments]:
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
Then you can update them independently with separate setState() calls:
componentDidMount() {
fetchPosts().then(response => {
this.setState({
// this "setState" call updates only "this.state.posts", but not "this.state.comments"!
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
// this "setState" call updates only "this.state.comments", but not "this.state.posts"!
comments: response.comments
});
});
}
The merging is shallow, so this.setState({comments}) leaves this.state.posts intact, but completely replaces this.state.comments.
- What is a "shallow merge" ?: stackoverflow
Hooks
- since React 16
- Hooks are functions
- Hooks are triggered
- by other actions
- when a component's props change
- Hooks are used to
- create data
- to trigger further changes
- Since this method of state management doesn't require you to use classes, developers can use Hooks to write shorter, more readable code that is easy to share and maintain.
- One of the main differences between Hooks and class-based state management is that there is no single object that holds all of the state. Instead, you can break up state into multiple pieces that you can update independently.
Built-in Hooks
- Built-in React Hooks: see API Reference
useState
- a function
- takes the initial state as an argument and returns an array with two items
- syntax:
const [variable, setFunction] = useState(defaultValue);- event handler function: the event handler function (i.e. the
functioninonClick={function}) must have the same scope as thesetFunction, therefore the event handler function must be defined inside the component function- if you define the arrow function inside of a prop (i.e. substitute
functioninonClick={function}with the definition offunction), React will- create a new function in every re-render
- which triggers a prop change
- which causes the component to re-render
- if you define the arrow function outside of a prop, you can use the
useCallbackHook which will memoize (cache) the function- this gives better performance than defining the function inside of a prop
- as a rule, the higher a component is likely to be in a tree, the greater the need for memoization.
- if you define the arrow function inside of a prop (i.e. substitute
- event handler function: the event handler function (i.e. the
useContext
useReducer
- specially designed to update the state based on the current state, in a manner similar to the
.reducearray method - The
useReducerHook is similar touseState, but when you initialize the Hook, you pass in a function the Hook will run when you change the state along with the initial data - The function - referred to as the reducer - takes two arguments:
- the state
- The other argument is what you will supply when you call the update function.
- A common pattern in reducer functions is to pass an object as the second argument that contains
- the name of the action and
- the data for the action.
- Inside the reducer, you can then update the total based on the action.
Redux
- a method of state management
- a third-party library
- also follows the React Hook naming convention, i.e. Redux Hooks also start with the prefix
use...- e.g.
useSelector,useStore
- e.g.
Best Practice in State Management
- It is best to only add information to
statethat you expect to change- This way, the information in
statewill be easier to keep track of as your application scales.
- This way, the information in
- minimize the different pieces of
stateby only saving related data in one place- In other words, try to avoid double references to the same data
Performance
Lazy Loading
From Wiki:
- also known as asynchronous loading
- a design pattern commonly used in computer programming and mostly in web design and development to defer initialization of an object until the point at which it is needed.
- It can contribute to efficiency in the program's operation if properly and appropriately used.
- This makes it ideal in use cases where network content is accessed and initialization times are to be kept at a minimum, such as in the case of web pages.
- For example, deferring loading of images on a web page until they are needed can make the initial display of the web page faster.
- The opposite of lazy loading is eager loading.
create-react-app (cra)
- CLI tool to quickly spin up a React app
- loosing popularity because of Next.js
- official doc
npx create-react-app my-app
# or:
npx create-react-app my-app --template [template-name]
# start the app:
npm start
Components of a cra Project
root component: src/App.js is the root component that is injected into the page. All components will start from here.
Rename a cra Project
- must rename multiple things:
- First, rename the project folder, eg.
mv -iv my-app/ my-new-app-name/ - Then, in
package.jsonandpackage-lock.jsonreplace all occurrences of the old namemy-app/withmy-new-app-name/.
- First, rename the project folder, eg.
cra Templates
- list of create-react-app (cra) templates
- using cra templates (
npx create-react-app my-app --template [template-name])
- using cra templates (
- official React doc source code
react-scripts
- for example
npm run start(ornpm start) runsreact-scripts startnpm run test(ornpm test) runsreact-scripts test- etc.
From: stackoverflow
react-scriptsis a set of scripts from thecreate-react-appstarter pack.create-react-apphelps you kick off projects without configuring, so you do not have to setup your project by yourself.npm startis a shortcut fornpm run start.npm runis used to run scripts that you define in thescriptsobject of yourpackage.json- if there is no
startkey in the scripts object, it will default tonode server.js - Sometimes you want to do more than the react scripts gives you, in this case you can do
react-scripts eject. This will transform your project from a "managed" state into a not managed state, where you have full control over dependencies, build scripts and other configurations.
{
...
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
...
}
Docusaurus
Creates a documentation page in React that is easy to maintain.
E.g. the create-react-app doc was built using Docusaurus (see source code on github).
Installation
For more details see docusaurus doc.
nvm install lts/hydrogen --latest-npm
nvm alias default lts/hydrogen
nvm use lts/hydrogen
Generate a new Docusaurus Website
npx create-docusaurus@latest my-website classic
Or create a blank template:
npm init docusaurus@latest
Local Development
$ cd my-website/
$ npm start
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
Local Build
Always build it locally before you push it to Gitlab.
npm run build
npm run serve