I have a parent component. This parent component combines a lot of if else control and a lot of mini jsx. I added my case. If I click the NextBtnText in the Modal component. It doesn't fire the first click. It needs a second click.
How can I fire the first click? What is wrong?
const StepperComponent = ({closeModal}) => {
/**
* there is some ,useState, useEffect and conditional functions
*/
const test = () => setActiveStep((prevActiveStep) => prevActiveStep + 1);
const NextBtnText = () => {
return (<Button
onClick={test}
disabled={firmType}
className={styles.StepperNextButton}
>
<span>{t("createFirm.buttons.next")}</span>
<KeyboardArrowRightIcon />
</Button>
);
};
const BackBtnText = () => {
return (
<>
<span>{t("createFirm.buttons.back")}</span>
</>
);
}
const RequestAssignmentBtnText = () => {
return (
<Button
onClick={handleSubmit}
disabled={firmType}
className={styles.StepperRequestButton}
>
<span>{t("createFirm.buttons.requestAssignment")}</span>
</Button>
)
}
return (
<div className={styles.StepperContainer}>
<Stepper activeStep={activeStep} className={styles.Steps}>
{steps.map((label, index) => {
return (
<Step key={index}>
<StepLabel >{label}<span className={styles.StepCountMobile}>{`(${index + 1} / ${steps.length})`}</span></StepLabel>
</Step>
);
})}
</Stepper>
{getStepContent(activeStep)}
<div className={styles.StepperButtons}>
<Button
disabled={activeStep === 0}
onClick={handleBack}
className={styles.StepperBackButton}
>
<span>{t("createFirm.buttons.back")}</span>
</Button>
{activeStep === steps.length - 1 ? (<RequestAssignmentBtnText />) : (<NextBtnText />)}
</div>
</div>
);
}
Yo're not calling your function. Simply do:
<Button
onClick={(event) => handleSubmit(event)}
>
// Your function has to look like this
const handleSubmit = (event) => {
// your code...
}
Or you can do this:
<Button
onClick={handleSubmit()}
>
// Your function has to look like this
const handleSubmit = () => {
return (event) => {
// your code...
}
}
What is the default value you set in useState() for activeStep?
I believe the onClick handler is working but the state is not what you expect on the first click. Maybe set the default value to 1 or 0 (I am not sure what is suitable for your use case).
const [activeStep, setActiveStep] = useState(1);
If the onClick is actually not working with the first click, try using plain HTML <input /> with test onClick handler and see if that works. It might have something to do with the Button component itself.
I fixed the onClick problem. The cause is about rerendered after disabled={firmType}. My button has a disabled attribute. I need to control after the checkbox is true/false.
Before :
{activeStep === steps.length - 1 ? (<RequestAssignmentBtnText />) : (<NextBtnText />)}
Solution :
{activeStep === steps.length - 1
?
<Button
onClick={handleSubmit}
className={styles.StepperNextButton}
>
<span>{t("createFirm.buttons.requestAssignment")}</span>
</Button>
:
<Button
onClick={handleNext}
disabled={firmType}
className={styles.StepperNextButton}
>
<span>{t("createFirm.buttons.next")}</span>
<KeyboardArrowRightIcon />
</Button>
}
Actually, I want to know what is different between Before and Solution.
Maybe someone can explain the issue of solution.
Related
I have a question - I have a list that is in a separate popup with a limited height. After this height, a side scroll appears. I need to scroll to a specific element automatically when that component is rendered. How to implement it? I just can't figure out how to scroll to a certain element.
Below is an example of my jsx code
<ul className={style.list}>
{itemsForRender?.length ? (
itemsForRender.map((item) => (
<li className={style.item} key={item.id}>
<button
type="button"
className={
activeItem === item.id
? `${style.button} ${style.activeClass}`
: style.button
}
onClick={() => selectItem(item.id)}
>
{item.name}
</button>
</li>
))
) : (
<p className={style.searchSubtitle}>
Just text
</p>
)}
</ul>
Use this code :
const ScrollDemo = () => {
const myRef = useRef(null)
const executeScroll = () => myRef.current.scrollIntoView()
// run this function from an event handler or an effect to execute scroll
return (
<>
<div ref={myRef}>Element to scroll to</div>
<button onClick={executeScroll}> Click to scroll </button>
</>
)
}
I had used Element.scrollIntoView() Method in React with making a reference of element i want to scroll to, here is the example:
function TestComponent() {
const testRef = useRef(null);
const scrollToElement = () => testRef.current.scrollIntoView();
// Once the scrollToElement function is run, the scroll will show the element
return (
<>
<div ref={testRef}>Element you want to view</div>
<button onClick={scrollToElement}>Trigger the scroll</button>
</>
);
}
you can use scrollIntoView() where you want scroll automatically
document.getElementById(element-id).scrollIntoView({behavior: 'smooth'})
you can use this in a useEffect() to run it when component is rendered
I hope this would be helpful. thanks
const scrollRef = useRef([]);
useEffect(() => {
// here you call the function scrollToSection and pass the id where you want to scroll
}, [])
const scrollToSection = id => {
if (scrollRef.current.length) {
scrollRef.current[id].scrollIntoView();
}
};
<ul className={style.list}>
{itemsForRender?.length ? (
itemsForRender.map((item) => (
<li className={style.item} ref={ref => (scrollRef.current[item.id] = ref)} key={item.id}>
<button
type="button"
className={
activeItem === item.id
? `${style.button} ${style.activeClass}`
: style.button
}
onClick={() => selectItem(item.id)}
>
{item.name}
</button>
</li>
))
) : (
<p className={style.searchSubtitle}>
Just text
</p>
)}
</ul>
I am trying to hide multiple divs with useSate.
They will be rendered random on the page, not from a list.
I have managed to do so by setting up different variables but couldn't find a more generic solution:
https://stackblitz.com/edit/react-t3shrc?file=src%2FApp.js
Also is there a way to close them when clicking outside?
Can you help please.
export default function App() {
const [isVisible, setIsVisible] = useState(false);
const [isVisible2, setIsVisible2] = useState(false);
const showInfo = (e, setIsVisible) => {
e.preventDefault();
setIsVisible(true);
};
const hideInfo = (e, setIsVisible) => {
e.preventDefault();
setIsVisible(false);
};
return (
<div>
<button
onClick={(e) => {
showInfo(e, setIsVisible);
}}
>
Show info 1
</button>
{isVisible && (
<div className="info">
Info 1
<button
onClick={(e) => {
hideInfo(e, setIsVisible);
}}
>
Close
</button>
</div>
)}
<br></br>
<br></br>
<button
onClick={(e) => {
showInfo(e, setIsVisible2);
}}
>
Show info 2
</button>
{isVisible2 && (
<div className="info">
Info 2
<button
onClick={(e) => {
hideInfo(e, setIsVisible2);
}}
>
Close
</button>
</div>
)}
</div>
);
}
I'm not 100% sure what you mean by a more 'generic' solution. Here is what comes to my mind:
First of all, we create a more complex object to basically hold all the variables / sections we encounter and use this as our state.
const initialVisibleAreas = {
area1: true,
area2: false
};
const [visibleAreas, setVisibleAreas] = useState(initialVisibleAreas);
Please note that this is propabably something you want to generate from your data using Object.keys(...) or mapping an array.
Next up, we create the functions for the buttons to use this new state accordingly:
// shows the element by given key
const showInfo = (event, key) => {
event.preventDefault();
setVisibleAreas({ ...visibleAreas, ...{ [key]: true } });
};
// hides the element by given key
const hideInfo = (event, key) => {
event.preventDefault();
setVisibleAreas({ ...visibleAreas, ...{ [key]: false } });
};
// sets every key to false to hide them all at once
const hideAllInfo = (event) => {
event.preventDefault();
const allFalse = Object.assign(
...Object.keys(visibleAreas).map((key) => ({ [key]: false }))
);
setVisibleAreas(allFalse);
};
Last but not least, we use them in jsx. This is basically one 'section':
<button
onClick={(e) => {
showInfo(e, 'area2');
}}
>
Show info 2
</button>
{
visibleAreas['area2'] && (
<div className="info">
Info 2
<button
onClick={(e) => {
hideInfo(e, 'area2');
}}
>
Close
</button>
</div>
);
}
To answer the last question; nothing is holding you to call 'hideAllInfo' inside a onClick handler of your surounding div. Sample is included in the modified stackblitz.
Have a complete look at the modified stackblitz
EDIT: In the case you want to close all areas by clicking the surrounding div, make sure to not propagate the button click event with:
event.stopPropagation();
I updated the stackblitz once again.
So basically I want to hide a Button when I press on it.
const Button=()=>{
const [hideButton, setHideButton]= React.useState(false)
function Button(){
setHideButton(false)
}
return(
<div>
<button onClick={setHideButton}> </button>
</div>
)
}
Initially make it true to show the button. hide it based on click.
const Button=()=>{
const [hideButton, setHideButton]= React.useState(true)
function handleClick(){
setHideButton(false)
}
return(
<div>
{hideButton && <button onClick={handleClick}>Click</button>}
</div>
)}
Or just put !(Not) before hideButton -> !hideButton and make it true on button click.
const Button=()=>{
const [hideButton, setHideButton]= React.useState(false)
function handleClick(){
setHideButton(true)
}
return(
<div>
{!hideButton && <button onClick={handleClick}>Click</button>}
<ChildComponent hideButton={hideButton} handleClick={handleClick}/>
</div>
)}
function ChildComponent ({hideButton, handleClick}) {
return (
<>
<button onClick={handleClick}>Child Button</button>
{!hideButton && <p>
This is content/paragraph which will be hidden
based on based on button click from parent component.
</p>}
</>
)
}
You can change as per your requirement i have passed function and state variable to child component. child button will toggle your content of child component and parent button hide itself once clicked.
Just conditionally render the button so it doesn't render if hideButton is true:
return(
<div>
{!hideButton && <button onClick={setHideButton}> </button>}
</div>
)
It is needed to use React hook for doing this.
const Button=()=>{
const [hideButton, setHideButton]= React.useState(false)
function hide(){
setHideButton(false)
}
return(
<div>
{!hideButton && <button onClick={hide}> </button>}
</div>
)
}
There are several things going wrong here.
This is the way your code should look (one of many ways)
const Button = () => {
const [hideButton, setHideButton] = React.useState(false)
function handleHideButton() {
setHideButton(true)
}
return (
<div>
{!hideButton ? <button onClick={handleHideButton}>Hide</button> : null}
</div>
)
}
What you were doing wrong:
const Button=()=>{
const [hideButton, setHideButton]= React.useState(false)
// You're creating this function called Button, but should be called something esle like handleHideButton
function Button(){
// You should be setting this to true when the button is clicked.
setHideButton(false)
}
return(
<div>
// here you should be calling the handleHideButton function defined above. Calling setHideButton isn't a good option here.
<button onClick={setHideButton}> </button>
</div>
)
}
I want to run some logic when a button is clicked and I want to change the state in that logic. So far react is giving me a to many rerenders error. Here is my code so far
const [current, setCurrent] = useState(0)
// const [theRest, theRest] = useState(false);
const next = (order, i) => {
let nextSlide = order + 1
setCurrent(nextSlide)
if (nextSlide > i) {
nextSlide = 0
}
}
{data.allSanitySlideDeck.edges.map(({ node: slide }, i) => (
<React.Fragment key={i}>
{/* {current === slide.order && ( */}
<>
<Card>
<h1>{slide.postTitle}</h1>
<h2></h2>
<p></p>
</Card>
<button>back</button>
<button onClick={next(slide.order, i)}>next</button>
</>
// )}
</React.Fragment>
))}
onClick expects a function inside the {} not the result of calling a function
Try changing to :
onClick={() => next(slide.order, i)}
What is basically happening is that function call is being made every time render() occurs and causes a state update which causes a new render call and therefore a race condition
I want to listen for click events on an arbitrary number of elements and inside the click event handler, I want to retrieve some info about the clicked element (info which was easily accessible during element creation).
A common solution to this problem is this:
render() {
return (
<div>
{this.props.users.map(user => (
<button onClick={() => this.buttonClicked(user.email)}>
{user.name}
</button>
))}
</div>
);
}
The flaw I see in this approach is that we're creating a new function for every element. Is that a problem worth solving? If it is, how do you feel about this solution:
render() {
return (
<div>
{this.props.users.map((user, index) => (
<button data-index={index} onClick={this.buttonClicked}>
{user.name}
</button>
))}
</div>
);
}
buttonClicked(event) {
const { index } = event.currentTarget.dataset;
const { email } = this.props.users[index];
// ...
}
Create another component and dispatch the event from it.
class Button extends React.PureComponent{
handleOnClick = ()=>{
const {onClick, ...rest} = this.props
if(onClick typeof ==='function'){
onClick(rest)
}
}
render(){
const {name} = this.props
return (
<button onClick={this.handleOnClick}>
{name}
</button>)
}
}
...
render() {
return (
<div>
{this.props.users.map(user => (
<Button {...user} key={user.email} onClick={this.buttonClicked} />
))}
</div>
);
}
I believe it is better to use a defined function instead of anonymous/inline functions, otherwise the onClick handler only gets created during the render stage.
In your second example, you can actually bind the argument to the handler:
render() {
return (
<div>
{this.props.users.map((user, index) => (
<button data-index={index} onClick={this.buttonClicked.bind(this, user.email)}>
{user.name}
</button>
))}
</div>
);
}
Secondly, wanted to point out that there is no performance issue with having many handlers. React uses one event handler at the root of your document.