Why react function component is behaving strangely? - reactjs

For example, I have a state and it can be changed by clicking on the buttons. I also have a log to track the rendering and rerendering of the component. When I click on the button and change the state, the log is triggered and everything is fine. But when I click 2 times on the same value, the log fires again. Clicking 3 times does nothing. Please explain what could be the reason
const App = () => {
const [state, setState] = useState(1);
console.log("render");
return (
<div>
<h3>{state}</h3>
<button onClick={() => {setState(1)}}>1</button>
<button onClick={() => {setState(2)}}>2</button>
</div>
);
};
export default App;

React rerenders a component if the state is changing, but in your example, when you click on a button again, the value of state does not change, because you want to change value of 2 to 2, so react does not rerender the component.

Related

Changing the React component state with same value on button click trigger re-render for the first two button clicks. Why UI re-rendering twice?

When I click on the 'Rerender UI' button then it prints in the console for the first two clicks. I think it should print in the console only for the first time when I click on the 'Rerender UI' button because on the button click the component state is changed so UI will re-render and the console log will be printed in the console. Why is it printing for the second click? StrictMode is off. See code:
export default function UseCallbackComp() {
const [stateVar, setStateVar] = useState<any>()
console.log("Parent Rerendered!")
return (
<>
<div>UseCallbackComp content</div>
<div>
<button
onClick={() => {
setStateVar(1)
}}
>
Rerender UI
</button>
</div>
</>
)
}
When I put the console log line inside useEffect like below it prints only for the first time 'ReRender UI' button is clicked which is the expected behaviour.
useEffect(() => {
console.log("Parent Rerendered!")
})
From the below two links I got to know whats the behaviour of react when useState is used:
stackoverflow question
gitHub discussion
There are two different cases for which useState behaves differently for same value.
Case 1: When useState used with same value as the initial value.
Result: No rerendering at all.
export default function LifecycleEvents() {
const [stateVar, setStateVar] = useState<any>(0)
console.log("Parent Rerendered!")
return (
<>
<button
onClick={() => {
setStateVar(0) //same value as the initial value
}}
>
Rerender UI
</button>
</>
)
}
Case 2: When useState used with different value first and then with the same value.
Result: First both Parent and child will be re-rendered. But for the second time, only the render function of Parent component will get called, Nothing else.
See code:
export default function LifecycleEvents() {
const [stateVar, setStateVar] = useState<any>(0)
console.log("Parent Rerendered!")
return (
<>
<button
onClick={() => {
setStateVar(1) //different value then initial value.
}}
>
Rerender UI
</button>
</>
)
}
Conclusion: Nothing to worry unless you have an expensive render method of the component. In that case use memo.

Why React function component call WITHOUT state change

here is the code: https://codesandbox.io/s/interesting-mirzakhani-h38g1k
import "./styles.css";
import { useState } from "react";
export default function App() {
const [state, setState] = useState(false);
console.log("executed");
return (
<>
<p>This component renders three (3) times:</p>
<p>1) First time is initial render.</p>
<p>
2) Second time is when clicking the button for the first time to update
state.
</p>
<p>
<strong>
3) Why does is render again when clicking the button second time and
settings the same state again?
</strong>
</p>
<p>Check console.</p>
<pre>state: {String(state)}</pre>
<button onClick={() => setState(true)}>setState(true)</button>
</>
);
}
And the situation here is:
initial (expected)
click the btn first time. State is updated and console. (expected)
click the btn second time, and state didn't change, but console again. (why?)
I found some source code about function dispatchSetState which is actually is setState in the demo. And here is the critical code is shown as the img.
First time click the btn, fiber.lanes === NoLanes is true, and then check the state is equal or not and then decide to update or not (objectIs).
Second time click the btn, fiber.lanes === NoLanes is false, and it just skip the comparison between old state and new state. Then the React start update.
So, what is exactly fiber.lanes is in second time? I found it is 1 which is SyncLane. here is ALL I found in source code.
diagram
I still don't know why react did this.

React onClick property not firing

I am trying to implement a simple component (an animated hamburger menu button). Its class needs to be updated when it is active or not. For that point, I'm using a state hook.
I want to add a onClick property to this button to invert its state. But this onClick function is never firing.
When I set the button active by myself, the button appearence changes as I wanted.
I am using fullPage.js in my project.
I saw a lot of topic about that, but no one answered to my problem...
Thanks for future answers !
Here is my code :
import React, { useState } from 'react';
import './hamburger.css';
const MenuButton = () => {
const [isMenuActive, setIsMenuActive] = useState(false);
return (
<button className={`hamburger hamburger--slider ${isMenuActive && "is-active"}`} type="button" onClick={() => setIsMenuActive(!isMenuActive)}>
<span className="hamburger-box">
<span className="hamburger-inner"></span>
</span>
</button>
);
};
export default MenuButton;
``
You are starting the state with false value. So it's not firing the onClick event because it's inactive from the get go
change the initial state useState(true)
Ok I found the problem.
I didn't noticed that I was using React v17 (beta). I downgraded to React 16 and my button works well !

react hooks, cant updating state via props

trying to update state variable('visible') via internal function(setVisible) in component. I checked the tutorıal and did same but its not updating after initialization of state.
Sandobx link here.
props.visible is true when user click ShowModal button. but value of visible in function component is still false. (I have checked the content on debugger)
code:
import Modal from '../Helpers/AppModal'
class Streams extends Component {
constructor(props) {
super(props)
this.state = { showModal: false }
}
componentDidMount() {
this.props.getStreams()
}
showDeleteModal = (isShow) =>
{
this.setState({ showModal: isShow });
}
onClickBackdrop = () => {this.setState({ showModal: false });}
render() {
return (
<div>
<button onClick={()=> this.showDeleteModal(true)} className="btn btn-danger">Delete</button>
<Modal visible={this.state.showModal} onClickBackdrop={this.onClickBackdrop} />
</div>
)
}
}
AppModal.js:
const AppModal = (props) => {
const [visible, setVisible] = useState(props.visible)
useEffect(() =>{
setVisible(props.visible)
},[props.visible])
debugger
return (
<Modal visible={visible} fade={true} onClickBackdrop={props.onClickBackdrop}>
<div className="modal-header">
<h5 className="modal-title">{props.title}</h5>
</div>
<div className="modal-body">
{props.body}
</div>
<div className="modal-footer">
<React.Fragment>
<button type="button" className="btn btn-default" onClick={()=>setVisible(false)}>
Close
</button>
</React.Fragment>
</div>
</Modal>
)
}
The argument passed to useStateis just the initial state. Pass a prop to it doesn't mean that the state will be synchronized with props. You can setup an effect to mirror those changes into your local state.
Currently your Modal only see visible from the local state, changing the props value won't cause Modal to change
//Inside child
useEffect(() =>{
setVisible(props.visible)
},[props])
Why should I use props instead of props.visible there?
The dependencies array exists to keep synchronicity, you're telling react:
"Hey, everytime one of those values changes re run this effect."
The problem is that React performs a shallow comparison (Object.is) between old and new props, uppon each render a new props object is generated which is what is triggering your effect in the first place.
React doesn't know how to "react" to nested changes. What is really changing here is props, react doesn't know (and doesn't care) about props.visible, passing it as a dependency is the same as passing []
Actually passing props as dependency is useless, since props changes every render you can omit the dependencies array, which will trigger the effect on each render
useEffect(() => {
setVisible(props.visible)
})
visible is a boolean.
Try changing the way you call setVisible like so:
setVisible(false)
instead of
setVisible({visible:false})
If this is a toggle switch then you should do this:
onClick={() => setVisible(!visible)}
Then it'll toggle on/off correctly.
You might want to set the initial value more explicitly though:
const [visible, setVisible] = useState(false);

Problem Too many re-renders.Try to render components in connection with a button and state hooks

I'm new to react and face following problem:
I have two buttons and want to render two different components. Pressing one button should set a variable true via state hook and the first component should be shown, on the other side the variable for the other component should be set to false. With this logic I try to render either one or the other component.
With this setup I get following Error Message:
Too many re-renders. React limits the number of renders to prevent an infinite loop.
I guess the solution is simple but I cannot see the why this causes a problem.
Thx for any help!
Here a simplified version of the relevant code:
import { Button } from "semantic-ui-react";
import React, { useState } from "react";
const App = () => {
const [showAggregated, setshowAggregated] = useState(true);
const [showDetailed, setshowDetailed] = useState(false);
return (
<div>
Test
<Button.Group>
<Button
size="mini"
onClick={(() => setshowAggregated(true), setshowDetailed(false))}
>
Aggregated Visualization{" "}
</Button>
<Button.Or />
<Button
size="mini"
onClick={(() => setshowDetailed(true), setshowAggregated(false))}
>
Detailed Visualization
</Button>
</Button.Group>
{/* Second Part of Page if Aggregated Component */}
{showAggregated && <div>Aggregated</div>}
{/* Second Part of Page if Detailed Component */}
{showDetailed && <div>Detailed</div>}
</div>
);
};
export default App;
onClick={(() => setshowDetailed(true), setshowAggregated(false))}
You are using Comma Operator above, which evaluates each of its operands.
In your case you have 2 operands: () => setshowDetailed(true) and setshowAggregated(false), so basically what happens is that each time the component is rendered, the second operand (setshowAggregated(false)) is called, which re-render the component which eventually ends up in an infinite loop.
What you really wanna do is this (an event handler that calls both functions):
onClick={() => {
setshowDetailed(true);
setshowAggregated(false));
}}
Same thing for the second button.

Resources