Reputation: 15
Code like this:
import React, {useState, useEffect} from 'react'
function App() {
const [menuitems, setMenuitems] = useState(null)
useEffect(() => {
console.log("Init")
setMenuitems(["menu1","menu2","menu3"])
},[])
const MenuItems = () => {
const renderMenuItems = () => {
if (menuitems && menuitems.length){
console.log("Render")
return menuitems.map((name) => {
return (
<button key={name}>{name}</button>
)
})
}
}
return (
renderMenuItems()
)
}
const [searchTi, setSearchTic] = useState('')
return (
<div className="App">
{menuitems && <MenuItems/>}
<p>Value: {searchTi}</p>
<input value={searchTi} onChange={(e) => setSearchTic(e.target.value)}/>
</div>
);
}
export default App;
When the input tag is used, the variable MenuItems is reloaded. What's wrong in my code? Why is it rerendering and how to prevent this from happening? As far as I understand, this happens after setting the variable "searchTi" through the function "setSearchTic". This updates the variable "menuitems " and reloads this section of code:
{menuitems && <MenuItems/>}
Upvotes: 1
Views: 107
Reputation: 540
I came across your question and it seemed interesting so I researched about it and finally, I found out that NEVER CREATE A COMPONENT INSIDE ANOTHER FUNCTION COMPONENT.
And I found an article written by Kuldeep Bora.
you can go through the article to understand this completely.
https://dev.to/borasvm/react-create-component-inside-a-component-456b
React components automatically re-render whenever there is a change in their state or props.
Function renderMenuItems
will re-create on every re-render and it is not an issue.
But if you don't want this behavior you can use the useCallback
hook, and then the function will re-create only when one of the dependencies will change.
useCallback hook docs: https://reactjs.org/docs/hooks-reference.html#usecallback
import React, {useState, useEffect} from 'react'
function App() {
const [menuitems, setMenuitems] = useState(null)
useEffect(() => {
console.log("Init")
setMenuitems(["menu1","menu2","menu3"])
},[])
// this function will re-create for every re-render
const renderMenuItems = () => {
if (menuitems && menuitems.length){
return menuitems.map((name) => {
return (
<button key={name}>{name}</button>
)
})
}
}
const [searchTi, setSearchTic] = useState('')
return (
<div className="App">
{renderMenuItems()}
<p>Value: {searchTi}</p>
<input value={searchTi} onChange={(e) => setSearchTic(e.target.value)}/>
</div>
);
}
export default App;
Upvotes: 0
Reputation: 7294
you are using MenuItems
like it was a component, but it's only a render function. should just call it like this:
import React, {useState, useEffect} from 'react'
function App() {
const [menuitems, setMenuitems] = useState(null)
useEffect(() => {
console.log("Init")
setMenuitems(["menu1","menu2","menu3"])
},[])
const renderMenuItems = () => {
if (menuitems && menuitems.length){
console.log("Render")
return menuitems.map((name) => {
return (
<button key={name}>{name}</button>
)
})
}
return null;
}
const [searchTi, setSearchTic] = useState('')
return (
<div className="App">
{renderMenuItems()}
<p>Value: {searchTi}</p>
<input value={searchTi} onChange={(e) => setSearchTic(e.target.value)}/>
</div>
);
}
export default App;
Compact example:
Also, there's no need to check to the menuitems.length
. Best way to render the menu items would be something like this:
const renderMenuItems = () => menuitems?.map((name) => <button key={name}>{name}</button>);
useMemo:
If you want to avoid re-render the menu items over and over, you should also use React.useMemo
like this:
const renderMenuItems = useMemo(() => menuitems?.map((name) => <button key={name}>{name}</button>), [menuitems]);
Note that it's now an object (similar to your JSX), and you should not call it, just put it as part of your JSX like this:
return (
<div className="App">
{renderMenuItems}
<p>Value: {searchTi}</p>
<input value={searchTi} onChange={(e) => setSearchTic(e.target.value)}/>
</div>
);
Upvotes: 2