How to instantly display data after an API call in react hooks - javascript

I don't understand why the second line, which reads data from the props, is not displayed as instantly as the first, i would like them to be displayed instantly
I update the state when a button is clicked, which calls api, data is coming in, the state is updating, but the second line requires an additional press to display
How to display both lines at once after a call? What's my mistake?
I'm using react hooks, and i know that required to use useEffect for re-render component, i know, that how do work asynchronous call,but i'm a little confused, how can i solve my problem, maybe i need to use 'useDeep effect' so that watching my object properties, or i don't understand at all how to use 'useEffect' in my situation, or even my api call incorrectly?
I have tried many different solution methods, for instance using Promise.all, waiting for a response and only then update the state
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./test";
ReactDOM.render(<App />, document.getElementById("root"));
app.js
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
const useDataApi = (initialState) => {
const [state, setState] = useState(initialState);
const stateCopy = [...state];
const setDate = (number, value) => {
setState(() => {
stateCopy[number].date = value;
return stateCopy;
});
};
const setInfo = async () => {
stateCopy.map((item, index) =>
getFetch(item.steamId).then((res) => setDate(index, res.Date))
);
};
const getFetch = async (id) => {
if (id === "") return;
const requestID = await fetch(`https://api.covid19api.com/summary`);
const responseJSON = await requestID.json();
console.log(responseJSON);
const result = await responseJSON;
return result;
};
return { state, setState, setInfo };
};
const Children = ({ data }) => {
return (
<>
<ul>
{data.map((item) => (
<li key={item.id}>
{item.date ? item.date : "Not data"}
<br></br>
</li>
))}
</ul>
</>
);
};
const InfoUsers = ({ number, steamid, change }) => {
return (
<>
<input
value={steamid}
numb={number}
onChange={(e) => change(number, e.target.value)}
/>
</>
);
};
function App() {
const usersProfiles = [
{ date: "", id: 1 },
{ date: "", id: 2 }
];
const profiles = useDataApi(usersProfiles);
return (
<div>
<InfoUsers number={0} change={profiles.setID} />
<InfoUsers number={1} change={profiles.setID} />
<button onClick={() => profiles.setInfo()}>Get</button>
<Children data={profiles.state} loading={profiles} />
</div>
);
}
export default App;
To get the data, just click GET
In this example, completely removed useEffect, maybe i don’t understand how to use it correctly.
P.s: Sorry for bad english

You don't need stateCopy, as you have it in the callback of the setState:
const setInfo = async () => {
// we want to update the component only once
const results = await Promise.all(
state.map(item => getFetch(item.steamId))
);
// 's' is the current state
setState(s =>
results.map((res, index) => ({ ...s[index], date: res.Date })
);
};

Related

Why setting the state through setTimeout doesn't render the correct props of a child component?

I have right here a component that should simply render a list of items. Also, the component includes an input that filters the list of items. If there is no items, or if the items are being loaded it should display a message.
import { useState } from "react";
export const List = ({ loading, options }) => {
const _options = options ?? [];
const [renderedOptions, setRenderedOptions] = useState(_options);
const [inputValue, setInputValue] = useState("");
function handleChange(event) {
setInputValue(event.target.value);
const filteredOptions = _options.filter((option) =>
option.toLowerCase().includes(event.target.value.toLowerCase())
);
setRenderedOptions(filteredOptions);
}
return (
<div>
<input type="text" value={inputValue} onChange={handleChange} />
<ul>
{renderedOptions.length > 0 ? (
renderedOptions.map((option) => <li key={option}>{option}</li>)
) : loading ? (
<li>Loading...</li>
) : (
<li>Nothing to show</li>
)}
</ul>
</div>
);
};
In App.js, I did a setTimeout, to mock a fetch call. However, there is a problem. Although I'm setting the asyncOptions state to be the new list of items, in my <List /> component the options do not seem to display properly.
import { List } from "./List";
import { useState, useEffect } from "react";
const ITEMS = ["list_1", "list_2", "list_3", "list_4", "list_5"];
export default function App() {
const [asyncOptions, setAsyncOptions] = useState([]);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
setIsLoading(true);
const timeoutId = setTimeout(() => {
setIsLoading(false);
setAsyncOptions(ITEMS);
}, 2000);
return () => clearTimeout(timeoutId);
}, []);
return <List options={asyncOptions} loading={isLoading} />;
}
What is this happening and what is/are the solution(s)?
Sandbox code here: https://codesandbox.io/s/async-list-j97u32
The first time when list component gets rendered, renderedOptions is initialized with []
const [renderedOptions, setRenderedOptions] = useState(options);
But when the state inside the App component changes, it triggers a re-render and henceforth it triggers re-render of List Component. So since you are passing options as argument to useState u might feel it'll update the state automatically but that's not the case
Note -> useState doesn't take into consideration whatever you are passing as argument except for the first time the component loads
.So the useState will return back the initial State which is [] every time the component re-renders
So if you want to see changes you have to add useEffect inside the List component and trigger a state update every time options changes
Change your code too this,
import { useState } from "react";
export const List = ({ options, loading }) => {
console.log("Listt", options);
const [renderedOptions, setRenderedOptions] = useState([...options]);
const [inputValue, setInputValue] = useState("");
console.log(renderedOptions);
function handleChange(event) {
setInputValue(event.target.value);
const filteredOptions = options.filter((option) =>
option.toLowerCase().includes(event.target.value.toLowerCase())
);
setRenderedOptions(filteredOptions);
}
useEffect(() => {
setRenderedOptions(options)
} , [options])
return (
<div>
<input type="text" value={inputValue} onChange={handleChange} />
<ul>
{renderedOptions.length > 0 ? (
renderedOptions.map((option) => <li key={option}>{option}</li>)
) : loading ? (
<li>Loading...</li>
) : (
<li>Nothing to show</li>
)}
</ul>
</div>
);
};
Basically, in the beginning, the value of options in an empty array, and the value put in state is a copy of that so the component is not listening to changes on the prop.
For some reason, you have to use the useEffect hook to actively listen to changes in the prop. By using the hook, when the API call returns something, it will set the state.(BTW, if anyone knows what is going on tell us)
I would recommend moving the API call to the List component, it would better encapsulate the logic
import { useEffect, useState } from "react";
export const List = ({ loading, options }) => {
const [renderedOptions, setRenderedOptions] = useState(options);
const [inputValue, setInputValue] = useState("");
useEffect(() => {
setRenderedOptions(options);
}, [options]);
function handleChange(event) {
setInputValue(event.target.value);
const filteredOptions = options.filter((option) =>
option.toLowerCase().includes(event.target.value.toLowerCase())
);
setRenderedOptions(filteredOptions);
}
return (
<div>
<input type="text" value={inputValue} onChange={handleChange} />
<ul>
{renderedOptions.length > 0 ? (
renderedOptions.map((option) => <li key={option}>{option}</li>)
) : loading ? (
<li>Loading...</li>
) : (
<li>Nothing to show</li>
)}
</ul>
</div>
);
};

Order of hooks error when rendering different components

React throws the following error when I am trying to render different components
Warning: React has detected a change in the order of Hooks called by GenericDialog. This will lead to bugs and errors if not fixed.
Previous render
Next render
useRef
useRef
useState
useState
useState
useState
useState
useState
useState
useState
useState
useState
useContext
useState
I do agree this would be inappropriate when I would be rendering the same component each time but with different order of hooks. What I am trying to achieve is render a different component each time so it is quite obvious the order of hooks won't be identical.
I have created this GenericDialog component which renders a multistep dialog.
import React, { useRef, useState, useEffect } from 'react';
import { DialogFooterNavigation } from './DialogFooterNavigation';
import { Dialog } from '../../../../Dialog';
import { Subheader } from '../../../../Subheader';
import { Loading } from '../../../../Loading';
export interface FooterConfiguration {
onContinue?: () => Promise<boolean | void>;
isContinueDisabled?: boolean;
continueLabel?: string;
isBackHidden?: boolean;
isCancelHidden?: boolean;
}
export interface HeaderConfiguration {
subheader?: string;
}
export interface DialogStepProps {
setHeaderConfiguration: (config: HeaderConfiguration) => void;
setFooterConfiguration: (config: FooterConfiguration) => void;
}
export type DialogStep = (props: DialogStepProps) => JSX.Element;
interface GenericDialogProps {
isShown: boolean;
hideDialog: () => void;
steps: DialogStep[];
header: string;
}
export const GenericDialog = ({
isShown,
hideDialog,
steps,
header,
}: GenericDialogProps) => {
const buttonRef = useRef(null);
const [step, setStep] = useState<number>(0);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [headerConfiguration, setHeaderConfiguration] = useState<HeaderConfiguration | undefined>(
undefined,
);
const [footerConfiguration, setFooterConfiguration] = useState<FooterConfiguration | undefined>(
undefined,
);
const [loadingMessage, setLoadingMessage] = useState<string>('');
const dialogBody = steps[step]({
setHeaderConfiguration,
setFooterConfiguration,
});
const nextStep = () => {
if (step < steps.length - 1) {
setStep(step + 1);
}
};
const prevStep = () => step > 0 && setStep(step -1);
const isBackPossible = step > 0;
const onBack = () => (isBackPossible || footerConfiguration?.isBackHidden ? undefined : prevStep);
const onContinue = async () => {
setIsLoading(true);
const result = await footerConfiguration?.onContinue?.call(undefined);
setIsLoading(false);
if (result === false) {
return;
}
nextStep();
};
return (
<Dialog isShown={isShown} onHide={hideDialog}>
<div>
{header}
{headerConfiguration?.subheader && (
<Subheader>{headerConfiguration.subheader}</Subheader>
)}
</div>
{isLoading && loadingMessage ? <Loading msg={loadingMessage} /> : dialogBody}
{!isLoading && (
<DialogFooterNavigation
onBack={isBackPossible ? onBack : undefined}
onContinue={onContinue}
isContinueDisabled={footerConfiguration?.isContinueDisabled}
/>
)}
</Dialog>
);
};
const FirstStep = (props: DialogStepProps) => {
// Here I need useContext
const { id, name } = useCustomerContext();
useEffect(() => {
props.setFooterConfiguration({
isContinueDisabled: !id || !name,
})
}, [id, name]);
return (
<>
<div>ID: {id}</div>
<div>Name: {name}</div>
</>
);
};
const SecondStep = (props: DialogStepProps) => {
// Here I don't need useContext but I do need useState
const [inputValue, setInputValue] = useState({});
useEffect(() => {
props.setFooterConfiguration({
isContinueDisabled: !inputValue,
});
}, [inputValue]);
return <input value={inputValue} onChange={(event) => setInputValue(event.target.value)} />;
}
const MyDialogExample = () => {
const [isDialogOpen, setIsDialogOpen] = useState(false);
const steps: DialogStep[] = [
FirstStep,
SecondStep,
];
return (
<>
<button onClick={() => setIsDialogOpen(true)}>Open Dialog</button>
<GenericDialog
isShown={isDialogOpen}
hideDialog={() => setIsDialogOpen(false)}
steps={steps}
header="Dialog example"
/>
</>
);
};
The problem is here:
const dialogBody = steps[step]({
setHeaderConfiguration,
setFooterConfiguration,
});
Try changing it to something like this:
const DialogBody = steps[step];
And then, in your return statement:
{isLoading && loadingMessage ? <Loading msg={loadingMessage} /> : <DialogBody setHeaderConfiguration={setHeaderConfiguration} setFooterConfiguration={setFooterConfiguration} />}
Please note that it can be done differently, like:
const DialogBody = steps[step];
const dialogBody = <DialogBody setHeaderConfiguration={setHeaderConfiguration} setFooterConfiguration={setFooterConfiguration} />;
And keeping your return statement unaltered.
Explanation
Your code isn't entirely wrong though. When working with functional components, there is a subtle difference between an actual component, a hook and a simple function that returns an instantiated component based on some logic. The problem is that you are mixing those three.
You can't manually instantiate a component by calling its corresponding function (just like you can't instantiate a class component by using the new operator). Either you use JSX (like <DialogBody />) or directly use React inner methods (Like React.createElement()). Both alternatives are different from just doing dialogBody(). For example, if you see the compiled JSX code you will note that <DialogBody /> compiles to code that uses React.createElement() and the latter returns a real React element instance containing many special properties and methods.
dialogBody() would work if its only goal was to return an instantiated element (Using one of the methods above) based on some logic. This implies not using any hook along with some other constraints.
Instead, your dialogBody 'function' contains hooks and it acts as a custom hook itself. This is why React complains about hooks execution order. You are executing hooks conditionally.

Reactjs prevent unwanted renderings with useCallback and memo

I have an array of objects called data. I loop this array and render the Counter component. Increment and decrement of the counter value are passed as props to the component.
But if I change the value in a one-component, the other two components also re-renders. Which is not needed. How do I prevent this behavior? I tried memo and useCallback but seems not implemented correctly.
Counter.js
import React, { useEffect } from "react";
const Counter = ({ value, onDecrement, onIncrement, id }) => {
useEffect(() => {
console.log("Function updated!", id);
}, [onDecrement, onIncrement]);
return (
<div>
{value}
<button onClick={() => onDecrement(id)}>-</button>
<button onClick={() => onIncrement(id)}>+</button>
</div>
);
};
export default React.memo(Counter);
Home.js
import React, { useState, useCallback } from "react";
import Counter from "../components/Counter";
export default function Home() {
const [data, setData] = useState([
{
id: 1,
value: 0,
},
{
id: 2,
value: 0,
},
{
id: 3,
value: 0,
},
]);
const onIncrement = useCallback(
(id) => {
setData((e) =>
e.map((record) => {
if (record.id === id) {
record.value += 1;
}
return record;
})
);
},
[data]
);
const onDecrement = useCallback(
(id) => {
setData((e) =>
e.map((record) => {
if (record.id === id) {
record.value -= 1;
}
return record;
})
);
},
[data]
);
return (
<div>
<h1>Home</h1>
{data.map((e) => {
return (
<Counter
value={e.value}
onDecrement={onDecrement}
onIncrement={onIncrement}
id={e.id}
/>
);
})}
</div>
);
}
I suspect useCallback & useMemo are not helpful in this case, since you're running an inline function in your render:
{data.map(e => <Counter ...>)}
This function will always returns a fresh array & the component will always be different than the previous one.
In order to fix this, I think you'd want to memoize that render function, not the Counter component.
Here's a simple memoized render function with useRef:
// inside of a React component
const cacheRef = useRef({})
const renderCounters = (data) => {
let results = []
data.forEach(e => {
const key = `${e.id}-${e.value}`
const component = cacheRef.current[key] || <Counter
value={e.value}
key={e.id}
onDecrement={onDecrement}
onIncrement={onIncrement}
id={e.id}
/>
results.push(component)
cacheRef.current[key] = component
})
return results
}
return (
<div>
<h1>Home</h1>
{renderCounters(data)}
</div>
);
In the codesandbox below, only the clicked component log its id:
https://codesandbox.io/s/vibrant-wildflower-0djo4?file=/src/App.js
Disclaimer: With this implementation, the component will only rerender if its data value changes. Other props (such as the increment/decrement callbacks) will not trigger changes. There's also no way to clear the cache.
Memoize is also trading memory for performance — sometimes it's not worth it. If there could be thousands of Counter, there're better optimiztion i.e. changing UI design, virtualizing the list, etc.
I'm sure there's a way to do this with useMemo/React.memo but I'm not familiar with it

how can i render a component with an API call with props react js

I am trying to render a component that call an api with the prop that i am passing, but i have this error: Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.
And other that say that it cannot read the property map of null, this is my code
import React, { useEffect } from "react";
const GetLeagues = async (country) => {
const url = `https://www.thesportsdb.com/api/v1/json/1/search_all_leagues.php?c=${country}&s=Soccer`;
const res = await fetch(url);
const { countrys } = await res.json();
return (
<div>
<ul>
{countrys.map((country, i) => {
return <li key={i}>{country.strLeague}</li>;
})}
</ul>
</div>
);
};
const Leagues = () => {
useEffect(() => {
GetLeagues();
}, []);
return (
<div>
<GetLeagues country={"Spain"} />
</div>
);
};
export default Leagues;
You shouldn't make side effects in the component body, you should use useEffect to make side effects, hence you shouldn't make your components async but instead, you can define your functions as async, use them in useEffect and then set your state.
function Leagues({ country }) {
const [countryData, setCountryData] = React.useState([]);
React.useEffect(() => {
async function getCountries() {
const url = `https://www.thesportsdb.com/api/v1/json/1/search_all_leagues.php?c=${country}&s=Soccer`;
const res = await fetch(url);
const { countrys } = await res.json();
setCountryData(countrys);
}
getCountries();
}, [country]);
return (
<div>
<ul>
{countryData.map((country, i) => {
return <li key={i}>{country.strLeague}</li>;
})}
</ul>
</div>
);
}
ReactDOM.render(
<Leagues country="Spain" />,
document.getelementById("root")
);
Since async functions is not supported in the snippet, here is a working version with .then promise chaining.
function Leagues({ country }) {
const [countryData, setCountryData] = React.useState([]);
React.useEffect(() => {
const url = `https://www.thesportsdb.com/api/v1/json/1/search_all_leagues.php?c=${country}&s=Soccer`;
fetch(url)
.then((res) => res.json())
.then(({ countrys }) => setCountryData(countrys));
}, [country]);
return (
<div>
<ul>
{countryData.map((country, i) => {
return <li key={i}>{country.strLeague}</li>;
})}
</ul>
</div>
);
}
ReactDOM.render(
<Leagues country="Spain" />,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root" />
Here is the component, just call it in App with <Leagues c='Spain'/>
I call the parameter c because country was clearly not readable having countrys everywhere.
import React, { useEffect, useState } from "react";
const Leagues = ({c}) => {
const [countrys, countrysSet] = useState(false);
useEffect(() => {
countrysSet(false);
const url = `https://www.thesportsdb.com/api/v1/json/1/search_all_leagues.php?c=${c}&s=Soccer`;
fetch(url).then( res => res.json()).then(countrysSet);
}, [c]);
if( countrys === false ) {
return <p>loading...</p>;
}
return (
<div>
<ul>
{countrys.map((country, i) => {
return <li key={i}>{country.strLeague}</li>;
})}
</ul>
</div>
);
};
export default Leagues;
I think you should break that code down in a few parts. Also, you cannot make api calls in the body of your function, else it will run every time your component is re rendered. Let me show you using your example:
import React, { useEffect, useState } from "react";
const GetLeagues = async (country) => {
// This helper function fetches your leagues
const url = 'your url'
const res = await fetch(url);
const { countries } = await res.json();
return countries;
};
const Leagues = () => {
const [leagues, setLeagues] = useState([]);
useEffect(() => {
async function init() {
// Declaring an extra function as useEffect
// cannot be async.
const countries = await GetLeagues();
setLeagues(countries);
}
init();
}, []);
return (
<div>
{leagues.map((country, i) => {
return <li key={i}>{country.strLeague}</li>;
})}
</div>
);
};
export default Leagues;
Note that now "GetLeagues" is just an utilitary function, not a React component. So that could be reused without rendering anything.
Also, your "Leagues" component handles all the necessary operations to render itself.

Which useEffect will be called after every Render?

I'm a beginner in React and stuck with some problem. I have several queries regarding this code.
Which UseEffect will be called after every render?
Why and How console.log() is called 13 times ?(Please find the screenshot below)
Why the fetched data is not shown in browser until I type something in the search bar?
App.js
import React, { useEffect } from "react";
import { useState } from "react";
import axios from "axios";
function App() {
const [monster, setMonster] = useState([]);
const [searchName, setName] = useState("");
const [filteredMonster, setFilter] = useState([]);
useEffect(() => {
async function fetchData() {
await axios.get(
"https://jsonplaceholder.typicode.com/users"
).then((resp)=>{
setMonster(resp.data);
})
console.log(monster);
}
fetchData();
}, []);
useEffect(()=>{
const mons = monster;
setFilter(mons.filter(mon =>
mon.name.toLowerCase().includes(searchName.toLowerCase())
));
}, [searchName]);
function changeName(event) {
setName(event.target.value);
}
console.log(monster);
const cunter = useRef(0);
return (
<div className="App">
<form>
<input
type="search"
name="searchName"
value={searchName}
onChange={changeName}
/>
</form>
{cunter.current++}
{filteredMonster&&filteredMonster.map((item, index) => (
<p key={index}>{item.name}</p>
))}
{monster&&!filteredMonster&&monster.map((item, index) => (
<p key={index}>{item.name}</p>
))}
</div>
);
}
export default App;
try this please. fetchData() will run only 1, searchName will run as many times you type on the screen.
TIP: To prevent this. add a timeoutdelay after user finishes typing to only render once instead of N times user presses a keyboard key.
import React, { useEffect } from "react";
import { useState } from "react";
import axios from "axios";
const URL = "https://jsonplaceholder.typicode.com/users"
function App() {
const [monster, setMonster] = useState([]);
const [searchName, setName] = useState("");
const [filteredMonster, setFilter] = useState([]);
useEffect(() => {
async function fetchData() {
await axios.get(URL).then((resp) => {
setMonster(resp.data);
})
console.log(monster);
}
fetchData();
}, []);
useEffect(() => {
if (monster.length > 0) {
const filter = mons.filter(({name}) =>
name.toLowerCase().includes(searchName.toLowerCase()));
setFilter(filter);
}
}, [searchName]);
function changeName(event) {
setName(event.target.value);
}
console.log(JSON.stringify(monster));
return (
<div className="App">
<form>
<input
type="search"
name="searchName"
value={searchName}
onKeyUp={(e) => changeName(e)}
/>
</form>
{monster.length > 0 &&
<div>{JSON.stringify(monster)}</div>
}
{filteredMonster && filteredMonster.map((item, index) => (
<p key={index}>{item.name}</p>
))}
{monster && !filteredMonster && monster.map((item, index) => (
<p key={index}>{item.name}</p>
))}
</div>
);
}
export default App;
This is using Reducer, removes the use of state.
import React, { useEffect, useReducer } from "react";
import axios from "axios";
const URL = "https://jsonplaceholder.typicode.com/users"
const reducer = (state, action) => {
switch(action.type){
case 'FETCH_DATA':
return {
...state,
monster: action.monster,
name: "",
}
case 'SEARCH_MONSTER':
return {
...state,
name: action.name,
}
case 'FILTER_MONSTER':
const filter = state.monster.filter(({name}) =>
name.toLowerCase().includes(searchName.toLowerCase()));
return {
...state,
filteredMonster: filter,
name: state.name,
}
}
};
function App() {
const [state, dispatch] = useReducer(reducer, {
monster: [],
filteredMonster: [],
name: '',
});
useEffect(() => {
async function fetchData() {
await axios.get(URL).then((resp) => {
dispatch({ type: 'FETCH_DATA', monster: resp.data});
})
console.log(monster);
}
fetchData();
}, []);
useEffect(() => {
if (monster.length > 0) dispatch({ type: 'FILTER_MONSTER'});
}, [stat.name]);
console.log(JSON.stringify(monster));
return (
<div className="App">
<form>
<input
type="search"
name="searchName"
value={state.name}
onKeyUp={(e) => dispatch({ type: 'SEARCH_MONSTER', name: e.target.value })}
/>
</form>
{state.monster.length > 0 &&
<div>{JSON.stringify(monster)}</div>
}
{state.filteredMonster && state.filteredMonster.map((item, index) => (
<p key={index}>{item.name}</p>
))}
{state.monster && !state.filteredMonster && monster.map((item, index) => (
<p key={index}>{item.name}</p>
))}
</div>
);
}
export default App;
1. Which UseEffect will be called after every render?
Ans: According to the react official doc useEffect does care about 3 lifecycle method namely componentDidMount componentDidUpdate and componentWillUnmount. So no matter what how many useEffect you have, all the effect hooks will execute when componentMount for the first time. But useEffect will execute further, only when it's dependency get updates else it will ignore
2. Why and How console.log() is called 13 times?
Ans: I tried to reproduce 13 times rerendering but I am not able to do so. But yes it's rerendering multiple times because in the 2nd useEffect on every Keystore the state is updating and because of that component is rerendering several times.
its happening something like this
changeName() → setName() → useEffect() → setFilter() → (on every keystore repeating same step) → ...loop
you can try debounce or throttling which can help you to avoid continuous Keystore hit by which no of rerendering can drastically reduce
Instead of using console.log, there is a hack to know the number of rerendering
declare the below code in the component
const cunter = useRef(0);
and then in the return block add {cunter.current++} by this you can see how many times your component is actually rerendering
3. Why the fetched data is not shown in the browser until I type something in the search bar?
This is because in your condition your checking !filteredMonster where filteredMonster is an array and !filteredMonster will return always false instead try Array length properties
filteredMonster.length === 0
{monster && !filteredMonster && monster.map((item, index) => (
<p key={index}>{item.name}</p>
))}
{(monster && filteredMonster.length === 0) && monster.map((item, index) => (
<p key={index}>{item.name}</p>
))}

Categories