Using Fetch API in JavaScript

Get Method using JavaScript Promise

fetch('http://example.com/movies.json')
  .then(response => response.json())
  .then(data => console.log(data));

Supplying request options

// Example POST method implementation:
async function postData(url = '', data = {}) {
  // Default options are marked with *
  const response = await fetch(url, {
    method: 'POST', // *GET, POST, PUT, DELETE, etc.
    mode: 'cors', // no-cors, *cors, same-origin
    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
    credentials: 'same-origin', // include, *same-origin, omit
    headers: {
      'Content-Type': 'application/json'
      // 'Content-Type': 'application/x-www-form-urlencoded',
    },
    redirect: 'follow', // manual, *follow, error
    referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
    body: JSON.stringify(data) // body data type must match "Content-Type" header
  });
  return response.json(); // parses JSON response into native JavaScript objects
}

postData('https://example.com/answer', { answer: 42 })
  .then(data => {
    console.log(data); // JSON data parsed by `data.json()` call
  });

Checking that the fetch was successful

fetch('flowers.jpg')
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not OK');
    }
    return response.blob();
  })
  .then(myBlob => {
    myImage.src = URL.createObjectURL(myBlob);
  })
  .catch(error => {
    console.error('There has been a problem with your fetch operation:', error);
  });

Supplying your own request object

const myHeaders = new Headers();

const myRequest = new Request('flowers.jpg', {
  method: 'GET',
  headers: myHeaders,
  mode: 'cors',
  cache: 'default',
});

fetch(myRequest)
  .then(response => response.blob())
  .then(myBlob => {
    myImage.src = URL.createObjectURL(myBlob);
  });

Headers

const content = 'Hello World';
const myHeaders = new Headers();
myHeaders.append('Content-Type', 'text/plain');
myHeaders.append('Content-Length', content.length.toString());
myHeaders.append('X-Custom-Header', 'ProcessThisImmediately');
const myHeaders = new Headers({
  'Content-Type': 'text/plain',
  'Content-Length': content.length.toString(),
  'X-Custom-Header': 'ProcessThisImmediately'
});
console.log(myHeaders.has('Content-Type')); // true
console.log(myHeaders.has('Set-Cookie')); // false
myHeaders.set('Content-Type', 'text/html');
myHeaders.append('X-Custom-Header', 'AnotherValue');

console.log(myHeaders.get('Content-Length')); // 11
console.log(myHeaders.get('X-Custom-Header')); // ['ProcessThisImmediately', 'AnotherValue']

myHeaders.delete('X-Custom-Header');
console.log(myHeaders.get('X-Custom-Header')); // null
fetch(myRequest)
  .then(response => {
     const contentType = response.headers.get('content-type');
     if (!contentType || !contentType.includes('application/json')) {
       throw new TypeError("Oops, we haven't got JSON!");
     }
     return response.json();
  })
  .then(data => {
      /* process your data further */
  })
  .catch(error => console.error(error));

References
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

Working with Multiple Slices in Redux Toolkit

import {configureStore, createSlice} from '@reduxjs/toolkit';

const initialCounterState = {counter: 0, showCounter: true};
const initialAuthState = {userName: "", isLoggedIn: false};

const counterSlice = createSlice({
    name: "counter",
    initialState: initialCounterState,
    reducers: {
        increment(state, action) {
            state.counter += action.payload
        },
        decrement(state, action) {
            state.counter -= action.payload
        },
        toggle(state, action) {
            state.showCounter = !state.showCounter
        }
    }
})

const authSlice = createSlice({
    name: "auth",
    initialState: initialAuthState,
    reducers: {
        login(state, action) {
            state.userName = action.payload.userName;
            state.isLoggedIn = true;
        },
        logout(state, action) {
            state.userName = "";
            state.isLoggedIn = false;
        }
    }
});

export const counterActions = counterSlice.actions;
export const authActions = authSlice.actions;

export const store = configureStore({
    reducer: {
        counter: counterSlice.reducer,
        auth: authSlice.reducer,
    }
});

References
https://stackoverflow.com/questions/67577835/same-action-triggering-in-multiple-slices-redux-toolkit

Working with Multiple State Properties in React Redux

store.js

import {configureStore} from '@reduxjs/toolkit';

const initialState = {counter: 0, showCounter: true};

const counterReducer = (state =initialState, action) => {

    if (action.type === "increment") {
        return {...state, counter: state.counter + action.payload};
    } else if (action.type === "decrement") {
        return {...state, counter: state.counter + action.payload};
    } else if (action.type === "toggle") {
        return {...state,showCounter: !state.showCounter}
    }

    return state;
}

export const store = configureStore({
    reducer: counterReducer
});

Counter.js

import {useSelector, useDispatch} from 'react-redux';

function Counter() {
    const counter = useSelector(state => state.counter);
    const showCounter = useSelector(state => state.showCounter);

    const dispatch = useDispatch();

    return (
        <div>
            {showCounter && <div>Counter : {counter}</div>}
            <div>
                <button onClick={() => dispatch({type: "increment", payload: 1})}>+1</button>
                <button onClick={() => dispatch({type: "decrement", payload: 1})}>-1</button>
                <button onClick={() => dispatch({type: "toggle"})}>Toggle</button>
            </div>
        </div>
    );
}

export default Counter;

Attaching Payloads to Actions in React Redux

Counter.js

import {useSelector, useDispatch} from 'react-redux';

function Counter() {
    const counter = useSelector(state => state.counter);
    const dispatch = useDispatch();

    return (
        <div>
            <div>Counter : {counter}</div>
            <div>
                <button onClick={() => dispatch({type: "increment", payload: 1})}>+1</button>
                <button onClick={() => dispatch({type: "decrement", payload: 1})}>-1</button>
                <button onClick={() => dispatch({type: "increment", payload: 5})}>+5</button>
                <button onClick={() => dispatch({type: "decrement", payload: 5})}>-5</button>
            </div>
        </div>
    );
}

export default Counter;

store.js

import {configureStore} from '@reduxjs/toolkit';

const counterReducer = (state = {counter: 0}, action) => {

    if (action.type === "increment") {
        return {...state, counter: state.counter + action.payload};
    } else if (action.type === "decrement") {
        return {...state, counter: state.counter + action.payload};
    }

    return state;
}

export const store = configureStore({
    reducer: counterReducer
});

References
https://redux.js.org/tutorials/fundamentals/part-2-concepts-data-flow#actions

Getting Started with React Redux

Installation

The recommended way to start new apps with React and Redux is by using the official Redux+JS template or Redux+TS template for Create React App, which takes advantage of Redux Toolkit and React Redux’s integration with React components.

# Redux + Plain JS template
npx create-react-app my-app --template redux

# Redux + TypeScript template
npx create-react-app my-app --template redux-typescript

or an existing react app:

# If you use npm:
npm install react-redux

Using React Redux

store.js

import {configureStore} from '@reduxjs/toolkit';

const counterReducer = (state = {counter: 0}, action) => {

    if (action.type === "increment") {
        return {counter: state.counter + 1};
    } else if (action.type === "decrement") {
        return {counter: state.counter - 1};
    }

    return state;
}

export const store = configureStore({
    reducer: counterReducer
});

index.js

import React from 'react';
import { createRoot } from 'react-dom/client';
import { Provider } from 'react-redux';
import { store } from './app/store';
import App from './App';
import reportWebVitals from './reportWebVitals';

const container = document.getElementById('root');
const root = createRoot(container);

root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

reportWebVitals();

Counter.js

import {useSelector, useDispatch} from 'react-redux';

function Counter() {
    const counter = useSelector(state => state.counter);
    const dispatch = useDispatch();

    const incrementHandler = () => {
        dispatch({type: "increment"});
    }

    const decrementHandler = () => {
        dispatch({type: "decrement"});
    }

    return (
        <div>
            <div>Counter : {counter}</div>
            <div>
                <button onClick={incrementHandler}>Increment</button>
                <button onClick={decrementHandler}>Decrement</button>
            </div>
        </div>
    );
}

export default Counter;

References
https://react-redux.js.org/introduction/getting-started

Introduction to Redux

This article will describe you a fundamental basics of redux in a simple node.js app

npm install redux

redux-demo.js

const redux = require("redux");

const counterReducer = (state = {counter: 0}, action) => {
    if (action.type === "increment") {
        return {
            counter: state.counter + 1,
        }
    } else if (action.type === "decrement") {
        return {
            counter: state.counter - 1,
        }
    }

    return state;
};

const counterSubscriber = () => {
    const newState = store.getState();
    console.log(newState);
};

const store = redux.createStore(counterReducer);

// in real world this will be subscribed in ui
store.subscribe(counterSubscriber);

// in real world this will be called from ui
store.dispatch({type: "increment"});
store.dispatch({type: "decrement"});
store.dispatch({type: "increment"});

References
https://redux.js.org/tutorials/fundamentals/part-1-overview

React.memo, useMemo and useCallback

React.memo

If your component renders the same result given the same props, you can wrap it in a call to React.memo for a performance boost in some cases by memoizing the result. This means that React will skip rendering the component, and reuse the last rendered result.

React.memo only checks for prop changes. If your function component wrapped in React.memo has a useState, useReducer or useContext Hook in its implementation, it will still rerender when state or context change.

 

Counter.tsx

function Counter() {

    const [counter, setCounter] = useState(0);

    console.log("Counter : " + Date.now());

    return (
        <div className="mx-4 my-2">
            <div>
                Count : {counter}
            </div>
            <div>
                <button type="button" className="btn"
                        onClick={event => setCounter(prevState => prevState + 1)}>Increase
                </button>
                <button type="button" className="btn"
                        onClick={event => setCounter(prevState => prevState - 1)}>Decrease
                </button>
            </div>
            <Logger/>
        </div>
    );
}

export default Counter;

Logger.tsx ( Before using React.memo )

function Logger() {
    return (
        <div>
            Logger Component
            {console.log("Logger : " + Date.now())}
        </div>
    );
}

export default Logger;

Logger.tsx ( After using React.memo )

function Logger() {
    return (
        <div>
            Logger Component
            {console.log("Logger : " + Date.now())}
        </div>
    );
}

export default React.memo(Logger);

 

Custom Comparison Function

By default it will only shallowly compare complex objects in the props object. If you want control over the comparison, you can also provide a custom comparison function as the second argument.

function MyComponent(props) {
  /* render using props */
}
function areEqual(prevProps, nextProps) {
  /*
  return true if passing nextProps to render would return
  the same result as passing prevProps to render,
  otherwise return false
  */
}
export default React.memo(MyComponent, areEqual);

useMemo

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]);

useCallback

Pass an inline callback and an array of dependencies. useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. shouldComponentUpdate).

useCallback(fn, deps) is equivalent to useMemo(() => fn, deps).

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

Should you use React.memo() or useMemo()?
Choosing between React.memo() and useMemo() should be straightforward. Now you have a good understanding of both of them.

  • Use React.memo to memoize an entire component.
  • Use useMemo to memoize a value within a functional component.

References
https://reactjs.org/docs/react-api.html#reactmemo
https://reactjs.org/docs/hooks-reference.html#usememo
https://reactjs.org/docs/hooks-reference.html#usecallback
https://blog.bitsrc.io/react-memo-vs-usememo-5730b90f0682

useRef Hook in React

useRef createRef
It is a hook. It is a function.
It uses the same ref throughout. It creates a new ref every time.
It saves its value between re-renders in a functional component. It creates a new ref for every re-render.
It persists the existing ref between re-renders. It does not persist the existing ref between re-renders.
It returns a mutable ref object. It also returns a mutable ref object.
The refs created using the useRef can persist for the entire component lifetime. The refs created using the createRef can be referenced throughout the component.
It is used in functional components. It is used in class components. It can also be used in functional components but might show inconsistencies.
function FancyInput(props, ref) {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));
  return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);

References
https://reactjs.org/docs/hooks-reference.html#useref
https://www.geeksforgeeks.org/difference-between-useref-and-createref-in-reactjs/
https://stackoverflow.com/questions/54620698/whats-the-difference-between-useref-and-createref

Using React Context

AuthContext

import React, {useState} from "react";

export interface AuthContextType {
    isLoggedIn: boolean,
    logIn: () => void,
    logOut: () => void,
}

const AuthContextDefault: AuthContextType = {
    isLoggedIn: false,
    logIn(): void {
    },
    logOut(): void {
    }
};

const AuthContext = React.createContext(AuthContextDefault);

export const AuthContextProvider = (props: any) => {

    // we need to initialize state from local storage because we should keep users logged in on page refresh
    const [isLoggedIn, setIsLoggedIn] = useState(() => {
        if (localStorage.getItem("isLoggedIn") === "1") {
            return true;
        }

        return false;
    });

    const logInHandler = () => {
        setIsLoggedIn(true);
        // save in local storage
        localStorage.setItem("isLoggedIn", "1");
    };

    const logOutHandler = () => {
        setIsLoggedIn(false);
        // remove from local storage
        localStorage.removeItem("isLoggedIn");
    }

    return <AuthContext.Provider value={{isLoggedIn: isLoggedIn, logIn: logInHandler, logOut: logOutHandler}}>
        {props.children}
    </AuthContext.Provider>
        ;
};

export default AuthContext;

index

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {BrowserRouter} from "react-router-dom";
import {AuthContextProvider} from "./context/AuthContext";

ReactDOM.render(
    <BrowserRouter>
        <React.StrictMode>
            <AuthContextProvider>
                <App/>
            </AuthContextProvider>
        </React.StrictMode>
    </BrowserRouter>
    ,
    document.getElementById('root')
);

reportWebVitals();

Login

import AuthContext from "../context/AuthContext";
import {useContext} from "react";

function Login() {

    const authCtx = useContext(AuthContext);

    return (
        <div className="m-2">
            <div>
                <span>Username : </span>
                <input type="text" className="input input-bordered w-full max-w-xs ml-2"
                       placeholder="Enter Username"/>
            </div>

            <div>
                <span>Password : </span>
                <input type="password" className="input input-bordered w-full max-w-xs ml-2"
                       placeholder="Enter Password"/>
            </div>
            <div className="mt-4">
                <button type="button" className="btn" onClick={authCtx.logIn}>Login</button>
                <button type="button" className="btn ml-2" onClick={authCtx.logOut}>Logout</button>
            </div>
        </div>
    );
}

export default Login;

User

import AuthContext from "../context/AuthContext";
import {useContext} from "react";

function User() {

    const authCtx = useContext(AuthContext);

    return (
        <div>
            {authCtx.isLoggedIn && "Logged in"}
            {!authCtx.isLoggedIn && "Logged out"}
        </div>
    )
}

export default User;

References
https://reactjs.org/docs/context.html