Rules of Hooks :

Pasted image 20240302225716.png

Pasted image 20240302225949.png


State Calculation + useState

Pasted image 20240302232547.png
Here in the example the rating was 8.6 which is greater than 8.
Pasted image 20240302232642.png

  • But we still get false as the output. This is because, the initial state will be set only when the component first mounts. When the component first mounts, the imdbRating will still be undefined, hence we get the false value.
  • On the second render this state will remain the same since we have not updated the value of it anywhere and isTop state is not re calculated on the second re render.
  • To get past through this problem we can use the useEffect hook, as seen in the images above.

Defining and Updating State :

Pasted image 20240303000328.png

useRef :

Suppose we want to focus on the search component (i.e. place the text cursor in it) each time our component mounts

We can do this by using the useEffect like this as well :
Pasted image 20240303001109.png

  • But this is not the react way of doing it. In react we tend to manually adding event listeners.
  • This is where useRef comes in handy.

useRef :

Pasted image 20240303003218.png
Pasted image 20240303003352.png
Pasted image 20240305183915.png

function Search({ query, setQuery }) {
  const inputEl = useRef(null); // we use useRef to get a reference to the input element

  useEffect(function () {
    // inputEl.cuurent = this is the dom element that we are referencing
    inputEl.current.focus(); // this will focus the input element as soon as the component is mounted
  }, []);

  return (
    <input
      className="search"
      type="text"
      placeholder="Search movies..."
      value={query}
      onChange={(e) => setQuery(e.target.value)}
      ref={inputEl} // now they are connected in a declarative way
    />
  );
}

Adding more features to it :

useEffect(function () {
    inputEl.current.focus();

    function callback(e) {

      // activeElement is the element currently being focused
      if(document.activeElement === inputEl.current)
        return; // → we are doing this to prevent the code below from being executed and setQuery("") from happening when we are searching for something and press the enter key to search. Otherwise as soon as we press the Enter key to search then the data we had put in the search will disappear.

      if (e.code === "Enter") 
      {
        inputEl.current.focus();
        setQuery(""); // to clear the input field after pressing enter
      }
    }

Other Use Cases of useRef :

Another use case of useRef is to simply give us a variable that is persisted across renders without triggering a re-render.

Suppose we want to remember how many times a user has pressed a button, but we do not want that information to be displayed or for it to re-render the component when that variable is updated.useRef is a perfect use case for this.

Info

Remember that we are not allowed to mutate the ref in Render Logic, so we have to use useEffect instead.

  /* ------------------------------------------------------ */
  // the idea is to store the number of times a user clicks the rating stars in the countRef variable, this variable since we are using a useRef has to persist it's value across renders and should not cause a re render on a change
  const countRef = useRef(0); // 0 is the initial value of countRef variable
  // let count = 0; // this will reset the count to 0 each time the component re-renders

  useEffect(function () {
    if(userRating) // As we do not want to run this effect when the userRating is empty or when the component mounts
      countRef.current = countRef.current += 1;
      console.log(countRef.current);
      
  }, [userRating]);

  /* ------------------------------------------------------ */

Custom Hooks :

Pasted image 20240305232301.png

example : useMovies custom hook :

`useMovie.js

import { useState, useEffect } from "react";

// this is a function only and not a component, so no need to use {} when accepting the props
export function useMovies(query) {
  const KEY = "f4feb5fa";

  const [movies, setMovies] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState("");

  useEffect(() => {
    const controller = new AbortController(); // this is a browser API, it's got nothing to do with React

    async function fetchMovies() {
      try {
        setIsLoading(true);
        setError("");

        const res = await fetch(
          `http://www.omdbapi.com/?apikey=${KEY}&s=${query}&`,
          { signal: controller.signal }
        );

        if (!res.ok)
          throw new Error("Something went wrong with fetching movies");

        const data = await res.json();

        if (data.Response === "False")
          // as API responds with {Response: 'False} when no search result is generated
          throw new Error("Movie not found");

        setMovies(data.Search);
        setIsLoading(false);
      } catch (err) {
        if (err.name !== "AbortError") {
          // we did this because when we cancel a fetch API request JS sees that as an error and then throws an error, we do not want this behaviour
          setError(err.message);
        }
      } finally {
        setIsLoading(false);
      } // this 'finally' block will always execute
    }
    if (query.length <= 2) {
      setMovies([]);
      setError("");
      return;
    }
    fetchMovies();

    // cleanup function : we want to cancel the current request to the API each time the query changes
    return function () {
      controller.abort();
    };
  }, [query]);

  return { movies, isLoading, error }; // returning an object 
}

App.js

const {movies, isLoading, error} = useMovies(query);
  • We can now use these variables anywhere in app.js

passing callback functions in useMovie (custom hook)

Pasted image 20240306012203.png

  • (query) ki jagah (query, callback) ho jayega
    • We'll call it using callback?.()

example : useLocalStorageState custom hook :

Folder 13 : Lecture 13

Pasted image 20240306022707.png
Pasted image 20240306022854.png