Dvashington
Dvashington

Reputation: 15

Why react rerender another variable?

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

Answers (2)

MOHD SHAHZAIB
MOHD SHAHZAIB

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

gilamran
gilamran

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

Related Questions