Rules of Hooks :
useState
Here in the example the rating was 8.6 which is greater than 8.
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. isTop
state is not re calculated on the second re render.useEffect
hook, as seen in the images above.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 :
useRef
comes in handy.
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
/>
);
}
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
}
}
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.
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]);
/* ------------------------------------------------------ */
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);
app.js
useMovie
(custom hook)(query)
ki jagah (query, callback)
ho jayega
callback?.()
useLocalStorageState
custom hook :Folder 13 : Lecture 13