1. Axios
If you know about the Axios security problem and someone asks this is for you, if you don’t know find it here, spoiler alert, it was solved a long time ago.
The solution: https://github.com/axios/axios/pull/1485
An alternative to Axios is the request lib or fetchAPI (but this relies on some problems with code fails that get at “then” instead of “catch” and needs extra steps like response.json() and doesn’t have interceptors or all kind of stuff that makes Axios and other libs easier).
2. Lazyloading
Code-Splitting is a feature supported by bundlers like Webpack, Rollup and Browserify (via factor-bundle) which can create multiple bundles that can be dynamically loaded at runtime.
How to do it:
To functions doing dynamic imports:
import("./math").then(math => {
console.log(math.add(16, 26));
});
Or to components using React.Lazy:
const OtherComponent = React.lazy(() => import('./OtherComponent'))
This will automatically load the bundle containing the OtherComponent when this component is first rendered. The lazy component should then be rendered inside a Suspense component, which allows us to show some fallback content (such as a loading indicator) while we’re waiting for the lazy component to load.
const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<section>
<OtherComponent />
<AnotherComponent />
</section>
</Suspense>
</div>
);
}
This component can be a placeholder component. Some examples at the semantic library: https://semantic-ui.com/elements/placeholder.html
3. Error Boundaries
Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed. Error boundaries catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them.
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
logErrorToMyService(error, errorInfo);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
Then you can use this as a wrapper component:
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
4. Webworkers
Web Workers makes it possible to run a script operation in a background thread separate from the main execution thread of a web application. The advantage of this is that laborious processing can be performed in a separate thread, allowing the main (usually the UI) thread to run without being blocked/slowed down.
var w;
function startWorker() {
if (typeof(Worker) !== "undefined") {
if (typeof(w) == "undefined") {
w = new Worker("demo_workers.js");
}
w.onmessage = function(event) {
document.getElementById("result").innerHTML = event.data;
};
} else {
document.getElementById("result").innerHTML = "Sorry! No Web Worker support.";
}
}
function stopWorker() {
w.terminate();
w = undefined;
}
5. IndexDB
IndexDB is a built-in database, much more powerful than localStorage. Key/value storage: value can be (almost) anything, multiple key types. Supports transactions for reliability. Supports key range queries, indexes. Can store much more data than localStorage.
Token, cookies, and JWT
To make the authentication token process we need to get two tokens: the access token and the session token. The access token is our main key to persist auth status. It just gives us access to receive the session token. The session token expires after a time at backend part. When this happens we need to make a new request with the access token to refresh the session token. Usually the code that the server send is 401 unauthorized.
With cookies this process is easier. you set the headers to contain “credentials” and it take the hidded cookies. They should be set as ont changeable by JS scripts and chrome hiddes it from cookies tab.
Bonus: If you have CORS problems to access the server you should use the properties access-control-allow-origin and/or access-control-allow-headers.
JSON Web Tokens (JWTs) make it easy to send read-only signed “claims” between services (both internal and external to your app/site). Claims are any bits of data that you want someone else to be able to read and/or verify but not alter.
6. Performance checkers
-
Audits: Lighthouse is an open-source, automated tool for improving the quality of web pages. You can run it against any web page, public or requiring authentication. It has audits for performance, accessibility, progressive web apps, and more.
-
Redux devtools: Redux DevTools for debugging application’s state changes.
-
React devtools: It allows you to inspect the React component hierarchies in the Chrome Developer Tools. You will get two new tabs in your Chrome DevTools: “⚛️ Components(to look into your components tree)” and “⚛️ Profiler(to make performance test in each component)”. It also says how many renders your component did.
-
Performance devtools: It is a tab of devtools that you can check the general performance of your application. Network devtools: You can check a list of all requests, and track the time they are taking to resolve. why-this-render: lib to check the quantity of components renderization.
-
Renderer devtools: It is an option at console options on devtools that you ca track some info about the rendering. One of them is FPS that checks how fluid is your page, the best value to FPS is at 60. So, if is lower than that that means you have space to work on performance.
Bonus articles: Devtools, React Performance and Profiler.
7. PWAs
Progressive web applications (PWAs) are a type of application software delivered through the web, built using common web technologies including HTML, CSS and JavaScript. They are intended to work on any platform that uses a standards-compliant browser. Functionality includes working offline, push notifications, and device hardware access, enabling creating user experiences similar to native applications on desktop and mobile devices. Since they are a type of web page or website known as a web application, there is no requirement for developers or users to install the web apps via digital distribution systems like Apple App Store or Google Play.
PWA’s rely on manifests(that contains some basic info about your app) and service workers that is a type of web worker. It’s essentially a JavaScript file that runs separately from the main browser thread, intercepting network requests, caching or retrieving resources from the cache, and delivering push messages.
8. Realtime
The WebSocket protocol, described in the specification RFC 6455 provides a way to exchange data between browser and server via a persistent connection. The data can be passed in both directions as “packets”, without breaking the connection and additional HTTP-requests. WebSocket is especially great for services that require continuous data exchange, e.g. online games, real-time trading systems and so on.
// First create the connection
let socket = new WebSocket("wss://javascript.info/article/websocket/demo/hello");
// Here you register an action when the connection starts
socket.onopen = function(e) {
alert("[open] Connection established");
alert("Sending to server");
socket.send("My name is John");
};
// When the socket is updated
socket.onmessage = function(event) {
alert(`[message] Data received from server: ${event.data}`);
};
// When it closes
socket.onclose = function(event) {
if (event.wasClean) {
alert(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
} else {
// e.g. server process killed or network down
// event.code is usually 1006 in this case
alert('[close] Connection died');
}
};
// And when some error happens
socket.onerror = function(error) {
alert(`[error] ${error.message}`);
};
The Server-Sent Events specification describes a built-in class EventSource, that keeps connection with the server and allows them to receive events from it.
Similar to WebSocket, the connection is persistent.
WebSocket and event source comparationEventSource is a less-powerful way of communicating with the server than WebSocket.
let eventSource = new EventSource("/events/subscribe");
eventSource.onmessage = function(event) {
console.log("New message", event.data);
// will log 3 times for the data stream above
};
// or eventSource.addEventListener('message', ...)
9. CSS performance
-
Don’t use CSS icons, use SVG.
-
Make individual selectors like classes and call it, that is better than call the children or complicated selectors.
-
Fewer elements to match are less computation to run. So when using children, use direct children or individual selectors.
-
Alphabetic order(a plugin or package can handle).
-
Use mixins instead extends(SASS).
-
Minify it.
-
Split the CSS imports and call it above the elements that use it. Component based CSS
10. Advanced hooks
useMemo Returns a memoized value. Pass a “create” function and an array of dependencies. useMemo will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render.
Remember that the function passed to useMemo runs during rendering. Don’t do anything there that you wouldn’t normally do while rendering. For example, side effects belong in useEffect, not useMemo.
If no array is provided, a new value will be computed on every render.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
useLayoutEffect signature is identical to useEffect, but it fires synchronously after all DOM mutations. Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint. Prefer the standard useEffect when possible to avoid blocking visual updates.
useReducer is an alternative to useState. Accepts a reducer of type (state, action) => newState, and returns the current state paired with a dispatch method. (If you’re familiar with Redux, you already know how this works.) useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. useReducer also lets you optimize performance for components that trigger deep updates because you can pass dispatch down instead of callbacks.
Here’s the counter example from the useState section, rewritten to use a reducer:
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
11. React memo
React memo is a high order component that basically checks if the component should re-render. If really is receiving some changes, so its parent can re-render and keeps it from re-render if is not necessary.
You use it by exporting the component like this:
export default React.memo(ComponentName)
12. TDD
Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle: requirements are turned into very specific test cases, then the software is improved so that the tests pass.
13. Big lists
Let's say you have a list with thousands of items to display and you don’t want to ruin the user experience that is using a phone. This video shows you two options to handle this.
https://www.youtube.com/watch?v=QhPn6hLGljU
My special thanks to all resources I did use here, like w3schools, javascript.info, MDN, react docs, and several videos from YouTube.