I want to do a movie search with the oMdb api using React Hooks. The result is not as expected. I seem to break some React Hooks rule that I don't understand.
Here is the code.
HOOK TO SEARCH
The Hook inside of a store. (If I use searchMovies('star wars') in a console.log I can see the result of star wars movies and series.)
import React, { useState, useEffect } from "react";
const useSearchMovies = (searchValue) => {
const API_KEY = "731e41f";
const URL = `http://www.omdbapi.com/?&apikey=${API_KEY}&s=${searchValue}`
// Manejador del estado
const [searchMovies, setSearchMovies] = useState([])
//Llamar y escuchar a la api
useEffect(() => {
fetch(URL)
.then(response => response.json())
.then(data => setSearchMovies(data.Search))
.catch((error) => {
console.Console.toString('Error', error)
})
}, []);
return searchMovies;
};
THE INPUT ON A SANDBOX
Here i have the input to search with a console log to see the result.
import React, { useState } from "react";
import searchMovies from "../store/hooks/useSearchMovies";
const Sandbox = () => {
const [search, setSearch] = useState('')
const onChangeHandler = e =>{
setSearch(e.target.value)
console.log('Search result', searchMovies(search))
}
const handleInput =()=> {
console.log('valor del input', search)
}
return (
<div>
<h1>Sandbox</h1>
<div>
<input type="text" value={search} onChange={onChangeHandler}/>
<button onClick={handleInput()}>search</button>
</div>
</div>
)
}
export default Sandbox;
You are breaking the rules of hooks by conditionally calling your hook in a nested function, i.e. a callback handler.
import searchMovies from "../store/hooks/useSearchMovies";
...
const onChangeHandler = e => {
setSearch(e.target.value);
console.log('Search result', searchMovies(search)); // <-- calling hook in callback
}
Only call hooks at the top level - Don’t call Hooks inside loops, conditions, or nested functions.
If I understand your code and your use case you want to fetch/search only when the search button is clicked. For this I suggest a refactor of your useSearchMovies
hook to instead return a search function with the appropriate parameters enclosed.
Example:
const useSearchMovies = () => {
const API_KEY = "XXXXXXX";
const searchMovies = (searchValue) => {
const URL = `https://www.omdbapi.com/?apikey=${API_KEY}&s=${searchValue}`;
return fetch(URL)
.then((response) => response.json())
.then((data) => data.Search)
.catch((error) => {
console.error("Error", error);
throw error;
});
};
return { searchMovies };
};
Usage:
import React, { useState } from "react";
import useSearchMovies from "../store/hooks/useSearchMovies";
const Sandbox = () => {
const [search, setSearch] = useState('');
const [movies, setMovies] = useState([]);
const { searchMovies } = useSearchMovies();
const onChangeHandler = e => {
setSearch(e.target.value)
};
const handleInput = async () => {
console.log('valor del input', search);
try {
const movies = await searchMovies(search);
setMovies(movies);
} catch (error) {
// handle error/set any error state/etc...
}
}
return (
<div>
<h1>Sandbox</h1>
<div>
<input type="text" value={search} onChange={onChangeHandler}/>
<button onClick={handleInput}>search</button>
</div>
<ul>
{movies.map(({ Title }) => (
<li key={Title}>{Title}</li>
))}
</ul>
</div>
);
};
export default Sandbox;