Skip to content

Reactjs hooks by Kuldeep

November 26, 2022 | 12:00 AM

Table of contents

Open Table of contents

useContext

Example:

Single file config

App.js

import "./App.css";
import { Routes, Route } from "react-router-dom";
import Pages from "./pages/page";
import Layout from "./layout";
import LoginPage from "./loginPage";
import RegisterPage from "./registerPage";
import { UserContextProvider } from "./context/userContext";
import CreatePost from "./pages/createPost";
import PostPage from "./pages/postPage";
import AuthorPage from "./pages/authorPage";
import EditPost from "./pages/editPost";

const App = () => {
  return (
    <UserContextProvider>
      <Routes>
        <Route path="/" element={<Layout />}>
          <Route index element={<Pages />} />
          <Route path={"/login"} element={<LoginPage />} />
          <Route path={"/register"} element={<RegisterPage />} />
          <Route path={"/create"} element={<CreatePost />} />
          <Route path={"/post/:id"} element={<PostPage />} />
          <Route path={"/author/:id"} element={<AuthorPage />} />
          <Route path={"/editPost/:id"} element={<EditPost />} />
        </Route>
      </Routes>
    </UserContextProvider>
  );
};

export default App;

UserContext.js

import { createContext, ReactNode, useContext } from 'react'
import { useState } from 'react'

type UserProps = {
    username: string;
    id: string;
    iat?: number;
    cover?: string;
    content?: string;
}

type ContextProps = {
    userInfo: UserProps | null
    setUserInfo: React.Dispatch<React.SetStateAction<UserProps | null>>
}

const UserContext = createContext<ContextProps>({ userInfo: null, setUserInfo: () => { } })

export const useUserContext = () => useContext(UserContext);

export function UserContextProvider({ children }: { children: ReactNode }) {
    const [userInfo, setUserInfo] = useState<UserProps | null>(null)

    return (
        <UserContext.Provider value={{ userInfo, setUserInfo }}>
            {children}
        </UserContext.Provider>)
}

To Access user

const { userInfo, setUserInfo } = useUserContext();

Two file config

DataContext.js

import { createContext } from "react";

const DataContext = createContext();

export default DataContext;

DataState.js

import React, { useState } from "react";
import DataContext from "./DataContext";

const DataState = props => {
  const [btnState, setBtnState] = useState(false);

  return (
    <DataContext.Provider value={{ btnState, setBtnState }}>
      {props.children}
    </DataContext.Provider>
  );
};

export default DataState;

App.js

import "./App.scss";
import DataState from "./Components/DataState";
import Example from "./ExampleComponent"

function App() {
  return (
    <div className="App">
      <DataState>
        <h3>Hello</h3>
        <Example>
      </DataState>
    </div>
  );
}

export default App;

Then in Example.js component where you have to use context

import React, { useContext } from "react";
import DataContext from "./DataContext";

const Example = props => {
  const {btnState, setBtnState} = useContext(DataContext);

  return (
    <div>
      <button onClick={()={setBtnState(!btnState)}}>Click me</button>
    </div>
  );
};

export default Example;

useReducer

example:

ReducerHelper.js

import { useReducer } from "react";
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();
  }
}

const ReducerHelper = () => {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: "increment" })}>+</button>
      <button onClick={() => dispatch({ type: "decrement" })}>-</button>
    </>
  );
};

export default ReducerHelper;

useMemo

example:

MemoHelper.js

import { useMemo, useState } from "react";

const delayFunction = dayOrNight => {
  const start = Date.now();
  let i = 0;
  while (i < 10000) {
    i += 0.0001;
  }
  const end = Date.now();
  console.log(`Execution time: ${end - start} ms`);

  if (dayOrNight === "") return "";
  if (dayOrNight === "morning") return "Good morning";
  else return "Just sleep";
};

const MemoHelper = () => {
  const [dayOrNight, setDayOrNight] = useState("");
  const [theme, setTheme] = useState(false);
  // const greeting = delayFunction(dayOrNight) //toggle to see effect of having an expensize function run without useMemo
  const greeting = useMemo(() => {
    return delayFunction(dayOrNight);
  }, [dayOrNight]);

  return (
    <div>
      <fieldset>
        <legend style={theme ? { color: "green" } : { color: "red" }}>
          Type something and check delay in console?
        </legend>
        <input
          type="text"
          value={dayOrNight}
          onChange={e => setDayOrNight(e.target.value)}
        />
      </fieldset>
      <div style={theme ? { color: "green" } : { color: "red" }}>
        <p>Greeting:- {greeting}</p>
      </div>
      <button onClick={() => setTheme(!theme)}>Change Color</button>
      <p>
        Buttton click causes re-render of component but delayFunction only runs
        if dependency changes
      </p>
    </div>
  );
};

export default MemoHelper;

useCallback

example:

CallbackParent.js

import { useCallback, useState } from "react";
import CallbackChild from "./CallbackChild";

const CallbackParent = () => {
  const [number, setNumber] = useState(0);
  const [dark, setDark] = useState(false);
  console.log("Parent Component redered");

  const handler = useCallback(() => {
    return [number, number + 1, number + 2];
  }, [number]); //api function call
  //const handler = ()=> { return [number, number + 1, number + 2]} //toggle this api* function to see it run on every render on parent without useCallback hook
  const theme = {
    backgroundColor: dark ? "#333" : "#fff",
    color: dark ? "#fff" : "#333",
  };

  return (
    <>
      <div style={theme}>
        <input
          type="number"
          value={number}
          onChange={e => setNumber(parseInt(e.target.value))}
        />
        <button onClick={() => setDark(prevDark => !prevDark)}>
          Toggle theme
        </button>
        <CallbackChild handler={handler} />
      </div>
    </>
  );
};

export default CallbackParent;

CallbackChild.js

import { useEffect, useState } from "react";

const CallbackChild = ({ handler }) => {
  const [items, setItems] = useState([]);

  useEffect(() => {
    setItems(handler());
    console.log("Child Component redered");
  }, [handler]);

  return (
    <>
      {items.map((item, index) => {
        return <div key={index}>{item}</div>;
      })}
    </>
  );
};

export default CallbackChild;

Note:

Remember useMemo and useCallback both take advantage of Referential Equality and they do have benifits & related side effects. Therefore, use them only when their use causes significant performence gain. useMemo return result, whereas useCallback returns a callback function which can take an argument too, passed from the child component. Both have similar syntax.


useNavigate

Install

useNavigate is part of react-router-dom hence we need to install that in order to use this hook

npm i react-router-dom

Usage

import { useNavigate } from "react-router-dom";

export default app = () => {
  const navigate = useNavigate();

  useEffect(() => {
    setTimeout(() => {
      // 👇 Redirects to home page, note the `replace: true`
      navigate("/", { replace: true });
    }, 3000);
  }, []);

  return <p>redirect home in 3 secs</p>;
};

useSearchParams

Installation useSearchParams is part of react-router-dom

npm i react-router-dom

Example

import { useSearchParams } from "react-router-dom";
import "./App.css";

function App() {
  const items = [
    "apple",
    "banana",
    "car",
    "dog",
    "elephant",
    "flower",
    "guitar",
    "house",
  ];
  const [search, setSearch] = useSearchParams({ q: "" });
  const query = search.get("q") || "";
  const filtered = items.filter(item => item.includes(query));

  return (
    <>
      <h1 className="text-[20px] text-blue-600">Hello react + vite app</h1>
      <div>
        <label htmlFor="q">Title</label>
        <input
          className="border-2 m-2 pl-1"
          type="text"
          id="q"
          value={query}
          onChange={e =>
            setSearch(
              prev => {
                prev.set("q", e.target.value);
                return prev;
              },
              { replace: true } // backbutton takes user to previous page
            )
          }
        />
      </div>
      <h1 className="font-semibold">Items</h1>
      <ul>
        {filtered.map(item => (
          <li key={item} className="list-disc">
            {item}
          </li>
        ))}
      </ul>
    </>
  );
}

export default App;