I just got into custom hooks and i created a custom hook that fetches data from an API.
interface DataProps<Type> {
results: Type[];
isLoading: boolean;
LIMIT: number;
offset: number;
}
interface Pokemon {
name: string;
url: string;
}
const useGetData = (url: string) => {
const [data, setData] = useState<DataProps<Pokemon>>({
results: [],
isLoading: true,
LIMIT: 80,
offset: 0
});
const fetchData = async () => {
const res = await fetch(
url +
new URLSearchParams({
limit: data.LIMIT + "",
offset: data.offset + ""
})
);
const { results } = await res.json();
setData((prevValue) => ({
...prevValue,
results: [...prevValue.results, ...results],
isLoading: false,
offset: prevValue.offset + prevValue.LIMIT
}));
};
useEffect(() => {
fetchData();
}, []);
return data;
};
In my App component, I use it like this:
export default function App() {
let data = useGetData(
`https://pokeapi.co/api/v2/pokemon?`
);
return (
<div className="App">
{data.isLoading && <div>Loading...</div>}
<ul>
{data.results.map((pokemon) => (
<li key={pokemon.name}>{pokemon.name}</li>
))}
</ul>
</div>
);
}
Works great, but this is only for displaying purposes. I would like, for example, to remove an element from the results array. How would I achieve that since I don't have a setData() to do that? From what I know, mutating state without a set...() is bad when you want the change in state to generate a rerender (like in my case, where I want the delete operation to remove the selected element from screen).
In your hook instead of
return data
use
return [data, setData]
Then in your component instead of
let data = useGetData('https://pokeapi.co/api/v2/pokemon?');
do
const [data, setData] = useGetData('https://pokeapi.co/api/v2/pokemon?');
You now have access to your setter in the component and can manipulate the state.