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 !
Related
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.
Should useEffect hook be used when it can be simplified using an event listener?
For example, in the below snippet code I use event listener to change some state and later useEffect hook to react to that state change and do some other thing
import { useEffect, useState } from "react";
export default function Foo() {
const [isActive, setIsActive] = useState(true);
useEffect(() => {
// do any kind of business logic
}, [isActive]);
return (
<>
<button
type="button"
className="secondary"
onClick={() => setIsActive(true)}
>
ACTIVATE
</button>
<button
type="button"
className="secondary"
onClick={() => setIsActive(false)}
>
DEACTIVATE
</button>
</>
);
}
Should I move useEffect logic to the onClick listeners?
I think if you can do something in event handlers then you should do them there instead of useEffect.
Here are some additional quotes.
Docs:
In React, side effects usually belong inside event handlers. Event
handlers are functions that React runs when you perform some
action—for example, when you click a button. Even though event
handlers are defined inside your component, they don’t run during
rendering! So event handlers don’t need to be pure.
If you’ve exhausted all other options and can’t find the right event
handler for your side effect, you can still attach it to your returned
JSX with a useEffect call in your component. This tells React to
execute it later, after rendering, when side effects are allowed.
However, this approach should be your last resort.
Also related quote by Dan Abramov:
To sum up, if something happens because a user did something,
useEffect might not be the best tool.
On the other hand, if an effect merely synchronizes something (Google
Map coordinates on a widget) to the current state, useEffect is a good
tool. And it can safely over-fire.
UseEffect run once when the component does mounting.
You can have state that triger the useEffect .
Should You move useEffect logic to the onClick listeners?
That depend on you, if you need to render your app , so no.
onClick function need do to something that depend to the page logic.
You don't need useEffect for simple operations, useEffect will also call on the component mount that you have to handle.
export default function Foo() {
const onClick = useCallback((isActive) => {
// fetch some data
// set that data to state
}, []);
return (
<>
<button type="button" className="secondary" onClick={() => onClick(true)}>
ACTIVATE
</button>
<button
type="button"
className="secondary"
onClick={() => onClick(false)}
>
DEACTIVATE
</button>
</>
);
}
I'm trying to reset my form data when a modal closes. I think part of my problem is that the form data <Mint> is in another component.
Honestly, any time the modal is shown or hidden I would like to reset the data.
So if there is a way I can reset the form data inside the toggleModalOne() function that would be awesome, but I can't figure out how.
Any advice is greatly appreciated.
// BuyIt.jsx
import React, { useState } from "react";
import TextLoop from "react-text-loop";
import Modal from "react-modal";
import Mint from "../../components/slider/mint.js";
const BuyIt = () => {
const [isOpen, setIsOpen] = useState(false);
function toggleModalOne() {
setIsOpen(!isOpen);
}
return (
<div className="div-buyNowBtn">
<button id="buyNowBtn" className="white-fill-bg btn-outline btn-lg" onClick={toggleModalOne}>
Buy Now
</button>
</div>
<Modal
isOpen={isOpen}
onRequestClose={toggleModalOne}
contentLabel="My dialog"
className="custom-modal"
overlayClassName="custom-overlay"
closeTimeoutMS={500}
>
<Mint/>
</Modal>
);
}
You could wrap your Modal around a check to see if isOpen is true. This will make the whole modal re-render each time rather than just hiding and showing it but it still being in the DOM (which is what I am assuming is happening).
{ isOpen &&
<Modal
isOpen={isOpen}
onRequestClose={toggleModalOne}
contentLabel="My dialog"
className="custom-modal"
overlayClassName="custom-overlay"
closeTimeoutMS={500}
>
<Mint></Mint>
</Modal>
}
If you want a parent function to reset a child's form state, you probably will want to lift the state up to the parent and pass it down as a prop.
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.
So basically i have a parent component which uses a child button component. Basically currently when the input validation is not correct it will keep the button disabled. However now I have tried to disable the button on click. The button is currently a pure component and i started to use hooks but not sure how i can still get the validation running.
Code is below
<ChildButton
onClick={() => {
this.checkSomething= this.checkCreds();
}}
enabled={this.validateInput()}
/>
My pure component currently looks like this:
export function AButton({ onClick, enabled, text }) {
const [disabled, setDisabled] = useState(!enabled);
function handleClick() {
setDisabled(!disabled);
//onClick();
}
return (
<Button
style={{ display: "block", margin: "auto" }}
id="next-button"
onClick={handleClick}
disabled={disabled}
>
{text}
</Button>
);
}
So i can get the disable button to work in both scenairos. As the enabled is always being passed down into this pure component so need to keep setting state of it.
I ended up using useEffect from react hooks
useEffect(() => setDisabled(!enabled), [enabled]);
This will check every time the enabled props is updated from the parent. Similar to how componentDidUpdate would work