When I click the increment button I expect the value displayed to go up but it stays the same
`
import * as React from 'react';
const MyComponent = () => {
var count = 0;
return (
<div>
<h1>Hello World</h1>
<button onClick={() => count++}>{count}</button>
</div>
);
};
`
You're not going to force a re render so your updated variable won't show.
import React, {useState} from 'react';
const MyComponent = () => {
const [count, setCount] = useState(0);
return (
<div>
<h1>Hello World</h1>
<button onClick={() => setCount(count + 1)}>{count}</button>
</div>
);
};
In React you have to use state for rendering or updating.
This is example of increasing and decreasing counter with useState hook.
import React, { useState } from 'react';
export function App(props) {
const [count, setCount] = useState(0);
const handleIncrease = () => {
setCount(count + 1);
};
const handleDecrease = () => {
setCount(count === 0 ? 0 : count - 1);
};
return (
<div className='App'>
<div>
<h1>Hello World</h1>
<button onClick={handleIncrease}>Increase</button>
<button onClick={handleDecrease}>Decrease</button>
<h3>Count is: {count}</h3>
</div>
</div>
);
}
You need to use useState hook so the component knows when to rerender and display new data.
I also recommend using useCallback hook to create memoized functions so you prevent unnecessary rerenders of your component (I know in this example it's an overkill but it's still good to know that).
You shouldn't, if possible, return arrow functions on your handlers like onClick - this will also cause the your component to not create new instances of your functions on each render, hence better performance (again not really relevant in this super simple case but a good thing to know nevertheless).
Here are some docs that you can read, these are a really good place to get started with React.
Here's also the code:
const MyComponent = () => {
const [count, setCount] = useState(0);
const handleCountIncrease = useCallback(() => {
setCount((c) => c + 1);
}, [setCount]);
return (
<div>
<h1>Hello World</h1>
<button onClick={handleCountIncrease}>{count}</button>
</div>
)
}
I see here you are using Functional component, so have you tried using React hooks? useState() for example:
import * as React from 'react';
const MyComponent = () => {
const [count, setCount] = useState(0);
return (
<div>
<h1>Hello World</h1>
<button onClick={() => setCount(count + 1)}>{count}</button>
</div> ); };
Try this maybe?
Related
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/>}
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>
);
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;
Is it possible to update one state in React and in the same render, can we use the updated value to update another state?
import "./styles.css";
import {useState} from 'react';
export default function App() {
const [num, setNum] = useState(0)
const [txt, setTxt] = useState(0)
handleClick= () =>{
setNum(num+1)
setTxt(num+1)
}
return (
<div className="App">
<input type="submit" value="button" onClick={handleClick} />
<h1>{num}</h1>
<h1>{txt}</h1>
</div>
);
}
In the above example, both num and txt are initially set to 0.
Clicking the button for the first time would increase both num and txt to 1 and it would update both to 2 when clicked again and so on.
Is there way to get the num updated to 1 as soon as setNum(num+1) is called, so that it can update it from 0 to 1 and so while calling setTxt(num+1), it can update it directly to 2?
Yes, you can do that using the useEffect hook with the num as a dependency.
Here's a CodeSandbox:
import { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
const [num, setNum] = useState(0);
const [txt, setTxt] = useState(0);
const handleClick = () => {
setNum(num + 1);
};
useEffect(() => {
if (num > 0) {
setTxt(num + 1);
}
}, [num]);
return (
<div className="App">
<input type="submit" value="button" onClick={handleClick} />
<h1>{num}</h1>
<h1>{txt}</h1>
</div>
);
}
If the actual state is just a number and not something complex like an array/object then you can simply do this:
const handleClick = () => {
setNum(num + 1);
setTxt(num + 2);
};
export const App2 =()=>{
const [counter,setCounter]=useState(0)
setTimeout(()=>setCounter(counter+1),1000)
return(
<div> {counter}
<button onClick={()=>{setCounter(counter+1)}}> Plus</button>
</div>
)
}
I recorded a video and uploaded it to youtube. I have no idea why this instability happens and switching tabs made it normal again. Anyone to enlighten? I just made a slight modification to this code
setTimeout causes an effect, so we should put it inside a useEffect() hook. Also, since setCounter depends on the previous counter state, we can pass an updater function to setCounter which receives the previous state value.
import React, { useCallback, useEffect } from "react";
export const App2 = () => {
const [counter, setCounter] = useState(0);
const increment = useCallback(() => {
setCounter((counter) => counter + 1);
}, []);
useEffect(() => {
const id = setTimeout(increment, 1000);
return () => clearTimeout(id);
}, [increment]);
return (
<div>
{" "}
{counter}
<button onClick={increment}> Plus</button>
</div>
);
};
import React, { useState, useEffect } from 'react';
const Test = () => {
const [ count, setCount ] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Count up!</button>
<ChildComponent />
</div>
)
}
const ChildComponent = () => {
useEffect(() => {
console.log('render!');
return () => console.log('unmounted...');
});
return (
<div>children</div>
)
};
export default Test;
Press the "Count up!" button.
log was output.
unmounted...
render!
ChildComponent is unchanged.
But rendered again.
why?
And how to prevent re-rendering?
thanks.
it re render because it's nested in a component that re render to prevent a nested component re render use React.memo()
const Test = () => {
const [ count, setCount ] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Count up! {count} </button>
<ChildComponent />
</div>
)
}
const ChildComponent = React.memo(() => {
useEffect(() => {
console.log('render!');
return () => console.log('unmounted...');
});
return (
<div>children</div>
)
});
export default Test;
you can read more about memo
Because the parent state is updated, the whole component will be re-rendered, include its children too, no matter if its children are changed or unchanged. You can use React.memo() to prevent your components from re-rendering. useCallback and useMemo can be used to memoize too.
See more here: How to memoize in React.
I am following this tutorial demonstrating react's 'useCallback' hook along with React.memo to prevent a function being render unnecessarily. To prove the concept we use useRef to console the number of renders. This worked with the function alone but i added a function to randomize the button background color and I can't seem to no prevent the rendering of both functions.
import React,{useState, useCallback, useRef} from 'react';
import './App.css';
const randomColor = () => `rgb(${Math.random()*255},${Math.random()*255},${Math.random()*255}`
const Button = React.memo(({increment, bgColor}) => {
const count = useRef(0)
console.log(count.current++)
return(
<button onClick={increment} style={{backgroundColor: bgColor}}>increment</button>
)
})
const App = React.memo(() => {
const [count, setCount] = useState(0)
const [color, setColor] = useState(`rgb(${Math.random()*255},${Math.random()*255},${Math.random()*255}`)
const increment = useCallback(() => {
setCount(previousCount => previousCount + 1)
setColor(randomColor)
},[setCount,setColor])
return (
<div className="App">
<header className="App-header">
<h2>{count}</h2>
<Button increment={increment} bgColor={color}>increment</Button>
</header>
</div>
);
})
export default App;
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
import React,{useState, useCallback, useRef} from 'react';
import './App.css';
const randomColor = () => `rgb(${Math.random()*255},${Math.random()*255},${Math.random()*255}`
const Button = React.memo(({increment, bgColor}) => {
const count = useRef(0)
console.log(count.current++)
return(
<button onClick={increment} style={{backgroundColor: bgColor}}>increment</button>
)
})
const App = React.memo(() => {
const [count, setCount] = useState(0)
const [color, setColor] = useState(`rgb(${Math.random()*255},${Math.random()*255},${Math.random()*255}`)
const increment = useCallback(() => {
setCount(previousCount => previousCount + 1)
setColor(randomColor)
},[setCount,setColor])
return (
<div className="App">
<header className="App-header">
<h2>{count}</h2>
<Button increment={increment} bgColor={color}>increment</Button>
</header>
</div>
);
})
export default App;
It the example in the video you mentioned, the Button component does not change, because the props stay the same all the time. In your example, the increment stays the same, but the problem is that the bgColor changes with each click.
That means, that if you rendered only the main component and not the Button component, the background would have to be the same, but because it receives different background color each time, it would not make sense.
React will always re-render the component if the props change (if you don't implement custom shouldUpdate lifecycle method).