React Modal Submit Form - reactjs

I'm trying to print out a simple buttton clicked text whenever the submit button is click on my reactstrap modal and somehow my code doesn't do anything, not sure what I have wrong.
I have attached a picture for better visualisation , I'm using reactstrap Modal.
import React, { useState } from "react";
import { Modal, ModalHeader, ModalBody, ModalFooter } from "reactstrap";
import Button from "./Button";
// NOTICE
// Modal is brought in with it's own trigger, so import the component where you want the trigger to be.
const ModalComponent = (props) => {
const {
buttonText,
title,
actionButtonText,
cancelButtonText,
children,
className,
} = props;
const [modal, setModal] = useState(false);
const toggle = () => setModal(!modal);
const alertshow = () => {
alert("button clicked");
};
const closeBtn = (
<button className="close" onClick={toggle}>
×
</button>
);
return (
<div>
<a className="btn_link" onClick={toggle}>
{buttonText}
</a>
<form onSubmit={alertshow}>
<Modal isOpen={modal} toggle={toggle} className={className}>
<ModalHeader className=" border-0" toggle={toggle} close={closeBtn}>
{title}
</ModalHeader>
<ModalBody className="text-left border-0">
<p className="modal-label">Please enter your email address</p>
{children}
</ModalBody>
<ModalFooter className="modal-footer border-0">
<Button className="btn_secondary modal-btn" onClick={toggle}>
{cancelButtonText}
</Button>{" "}
<input
className="btn btn_primary modal-btn"
type="submit"
value={actionButtonText}
/>
</ModalFooter>
</Modal>
</form>
</div>
);
};
export default ModalComponent;

Its happening form should be part of modal not modal should be part of form. This is why its not referencing onSubmit. You need to do this:
<Modal isOpen={modal} toggle={toggle} className={className}>
<form onSubmit={alertshow}>
...rest all content
</Form>
</Modal>
Here is full code:
import React, { useState } from "react";
import "./styles.css";
import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from "reactstrap";
// NOTICE
// Modal is brought in with it's own trigger, so import the component where you want the trigger to be.
const ModalComponent = (props) => {
const {
buttonText,
title,
actionButtonText,
cancelButtonText,
children,
className
} = props;
const [modal, setModal] = useState(false);
const toggle = () => setModal(!modal);
const alertshow = () => {
alert("button clicked");
};
const closeBtn = (
<button className="close" onClick={toggle}>
×
</button>
);
return (
<div>
<div onClick={toggle}>{buttonText}</div>
<Modal isOpen={modal} toggle={toggle} className={className}>
<form onSubmit={alertshow}>
<ModalHeader className=" border-0" toggle={toggle} close={closeBtn}>
{title}
</ModalHeader>
<ModalBody className="text-left border-0">
<p className="modal-label">Please enter your email address</p>
{children}
</ModalBody>
<ModalFooter className="modal-footer border-0">
<Button className="btn_secondary modal-btn" onClick={toggle}>
{cancelButtonText}
</Button>{" "}
<input
className="btn btn_primary modal-btn"
type="submit"
value={actionButtonText}
/>
</ModalFooter>
</form>
</Modal>
</div>
);
};
export default function App() {
return (
<div className="App">
<ModalComponent
title="Hello"
cancelButtonText="Cancel"
actionButtonText="Submit"
buttonText="testing"
/>
</div>
);
}
Here is the demo: https://codesandbox.io/s/fervent-bash-51lxe?file=/src/App.js:0-1826

Accepted answer doesn't work when Modal is scrollable.
Here is how to resolve the issue:
<Modal show={ show } onHide={ onClose }
scrollable={ true }
onSubmit={ handleSubmit(onSave) }
dialogAs={ FormWrappedModal }>
<Modal.Header closeButton>
<Modal.Title>some title</Modal.Title>
</Modal.Header>
<Modal.Body>some body</Modal.Body>
<Modal.Footer>some body</Modal.Footer>
</Modal>
We need to introduce custom component FormWrappedModal for that purpose:
const FormWrappedModal = ( props: any)=>{
return (
<form onSubmit={ props.onSubmit }>
<Modal.Dialog { ...props } />
</form>
);
};

Related

adding specefic data from in array without loop

i want get all the data from dataFromes and put it in Modal.title but what i did using map it only show the first in array of dataFromes
import React, { useState } from 'react';
import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal';
import Form from 'react-bootstrap/Form';
import { useSelector } from 'react-redux';
function ViewPop(props) {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
const dataFromes = useSelector(state => state.data.value);
const expenseForms = useSelector(state => state.expend.value);
return (
<div>
<Button variant= {props.outline} onClick={() => {
handleShow();
}} className= "mx-1">
{props.text}
</Button>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<div className='title flex'>
{dataFromes.map((data, index) => (
<Modal.Title>Expenses - {data.text}</Modal.Title>
))}
<Button variant="outline-danger" className='ml-2'>Delete</Button>
</div>
</Modal.Header>
<Form className="mx-3 mt-3">
<Form.Group className="mb-3">
{expenseForms ? expenseForms.map(expense => (
<div key={expense.text} className='expense flex justify-between items-center mb-3'>
<span className='fs-4'>{expense.text}</span>
<span className='fs-5'><span>$</span>{expense.amount} <Button className= "text-center mb-[9px] w-7 ml-1" size="sm" variant="outline-danger"><span className="text-[10px]">X</span></Button></span>
</div>
)) : null}
</Form.Group>
</Form>
{/* <Modal.Title>Expenses - {data.text}</Modal.Title> */}
</Modal>
</div>
)
};
export default ViewPop;
hello i want get all the data from dataFromes and put it in Modal.title but what i did using map it only show the first in array of dataFromes

React: How to update a variable that depends on a state changes

I'm trying to use React to make a Dungeons and Dragons Character Sheet (click here for a preview). One component I am stuck on is the ability modifier, shown below:
With React.useState(), I can automatically changes the number "7" by clicking the arrows
I've cracked the basics of useState(). I can now change the variable titled count in the red circle if I click the MUI arrow buttons. I want the string value (now set to "15") to be dynamic, and equal to (count-10)/2.
I am trying to use onChange to make it work, but no luck. My code for the component is shown below. What am I doing wrong?
`
import React from "react"
import ArrowBackIosIcon from '#mui/icons-material/ArrowBackIos';
import ArrowForwardIosIcon from '#mui/icons-material/ArrowForwardIos';
import Button from '#mui/material/Button';
export default function StatBox(props) {
const[count, setCount] = React.useState(8)
const incrementCount = () => {
setCount(count+1);
}
let modifier = "15"
if (count && !isNaN){
const modifierNum = Math.floor(Number(count)-10/2)
if(modifierNum > 0){
modifier = "+" + modifierNum
}
else{
modifier = modifierNum.toString()
}
}
const decrementCount = () => {
setCount(count-1);
}
return (
<div className="col text-center border border-danger rounded mt-2 mb-2">
<label>{props.stats}</label>
<div className="d-and-d-statbox-modifier">{modifier}</div>
<div className="row justify-content-center mb-2">
<Button
onClick={decrementCount}
onChange={(e) => count.onChange(count, e.target.value)}
size="sm"
variant="contained"
color="error"
>
<ArrowBackIosIcon fontSize="small" />
</Button>
<div>
<span className="mx-1 border-danger text-center dndstatrow">
{count}
</span>
</div>
<Button
onClick={incrementCount}
onChange={(e) => count.onChange(count, e.target.value)}
size="sm"
variant="contained"
color="error"
>
<ArrowForwardIosIcon fontSize="small" />
</Button>
</div>
</div>
);
}
`
Solved!
import React from "react"
import ArrowBackIosIcon from '#mui/icons-material/ArrowBackIos';
import ArrowForwardIosIcon from '#mui/icons-material/ArrowForwardIos';
import Button from '#mui/material/Button';
export default function StatBox(props) {
const[count, setCount] = React.useState(8)
const incrementCount = () => {
setCount(count+1);
}
let modifierNum = Math.floor(((count)-10)/2)
let modifier = ""
if(modifierNum > 0){
modifier = "+" + modifierNum
}
else{
modifier = modifierNum.toString()
}
const decrementCount = () => {
setCount(count-1);
}
return (
<div className="col text-center border border-danger rounded mt-2 mb-2">
<label>{props.stats}</label>
<div className="d-and-d-statbox-modifier">{modifier}</div>
<div className="row justify-content-center mb-2">
<Button
onClick={decrementCount}
size="sm"
variant="contained"
color="error"
>
<ArrowBackIosIcon fontSize="small" />
</Button>
<div>
<span className="mx-1 border-danger text-center dndstatrow">
{count}
</span>
</div>
<Button
onClick={incrementCount}
size="sm"
variant="contained"
color="error"
>
<ArrowForwardIosIcon fontSize="small" />
</Button>
</div>
</div>
);
}

Unable to focus form element on modal open with react-hook-form

I'm trying to solve a problem that seems quite straight forward. I want to focus a form element as soon as a modal opens.
Since react-hook-form does not provide a direct handle to the form refs, I'm trying to use a useEffect hook and react-hook-forms howngrown setFocus functions but I keep seeing the error:
s.focus is not a function
Code
--> Link to coding sandbox
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import "./styles.css";
import {
ChakraProvider,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalFooter,
ModalBody,
Button,
ModalCloseButton,
useDisclosure
} from "#chakra-ui/react";
export default function App() {
const { setFocus, register } = useForm();
const { isOpen, onClose, onOpen } = useDisclosure();
useEffect(() => {
if (!isOpen) return;
setFocus("name");
}, [setFocus, isOpen]);
return (
<ChakraProvider>
<div className="App">
<Button onClick={onOpen}>Open</Button>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Modal Title</ModalHeader>
<ModalCloseButton />
<ModalBody>
<input {...register("name")} />
</ModalBody>
<ModalFooter>
<Button colorScheme="blue" mr={3} onClick={onClose}>
Close
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</div>
</ChakraProvider>
);
}
Maybe setFocus can't run because the modal content (and the input inside) is first unmounted.
An easy solution using setFocus is to make the form as an independant component :
const Form = () => {
const { setFocus, register } = useForm();
useEffect(() => {
setFocus("name");
}, [setFocus]);
return <input {...register("name")} />;
};
and then include it inside the modalBody.
Fixed sandbox link
Read the Chakra documentation there is one prop initialFocusRef, you can pass the ref to the element you want to focus on initially
Chakra provides 2 props for this use case:
initialFocusRef: The ref of the component that receives focus when the modal opens.
finalFocusRef: The ref of the component that receives focus when the modal closes.
import { useEffect, useRef } from "react";
import { useForm } from "react-hook-form";
import "./styles.css";
import {
ChakraProvider,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalFooter,
ModalBody,
Button,
ModalCloseButton,
useDisclosure
} from "#chakra-ui/react";
export default function App() {
const { setFocus, register } = useForm();
const { isOpen, onClose, onOpen } = useDisclosure();
const initialRef = useRef();
useEffect(() => {
//setFocus("name");
}, [setFocus, isOpen]);
return (
<ChakraProvider>
<div className="App">
<Button onClick={onOpen}>Open</Button>
<Modal isOpen={isOpen} onClose={onClose} initialFocusRef={initialRef}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Modal Title</ModalHeader>
<ModalCloseButton />
<ModalBody>
<input {...register("name")} ref={initialRef} />
</ModalBody>
<ModalFooter>
<Button colorScheme="blue" mr={3} onClick={onClose}>
Close
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</div>
</ChakraProvider>
);
}
UPDATED BASED ON COMMENT
By default, Chkra focus enabled elements if you just add tabIndex to your elements and give order value appropriately then it will get focused. Previously close button was focused by default, now in the below code you will notice I have given tabIndex=2 to the close button and tabIndex=1 to the input element and it is working as expected.
<ChakraProvider>
<div className="App">
<Button onClick={onOpen}>Open</Button>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Modal Title</ModalHeader>
<ModalCloseButton tabIndex="2" />
<ModalBody>
<input tabIndex="1" {...register("name")} />
</ModalBody>
<ModalFooter>
<Button colorScheme="blue" mr={3} onClick={onClose}>
Close
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</div>
</ChakraProvider>
If you want to use that using ref then you can check "How to share ref usage?", maybe it will help you.

Show popup from another component using functions in react (.jsx)?

Hello there I'm new with Reactjs and just have a quick question.
I have a modal component that I want to show up when a function is called from another component.
The modal component
const Popup = (props) => {
const [open, setOpen] = React.useState(false);
const toggle = () => {
setOpen(!open);
};
return (
<div>
<Button onClick={toggle}>Click Me</Button>
<Modal className='successModal text-center' open={open} centered={true} size='lg'>
<ModalHeader>{props.title}</ModalHeader>
<ModalBody>
<div>
<img src={successIcon} alt='Success Icon' />
<div className='container my-4'>
<h4>{props.body}</h4>
</div>
</div>
<Button onClick={toggle}>Close</Button>
</ModalBody>
</Modal>
</div>
);
};
export default Popup;
This is the function that I need to show up the popup when it's called
const finishPayment = () => {
console.log('just checking if this function is called properly');
return (
<Popup
toggle={this.toggle}
title='Success'
body='Welcome, everything was fine.'
/>
);
};
Can someone help me how to figure out this?
I think I need to pass something as props, but not sure what.
Thanks!
Your buttons are using "onClick" events, therefore, you must pass a function as argument, but you are passing a constant. Convert your "toggle()" arrow function to function or implement another arrow function inside the "onClick" events, so as in the examples below:
With arrow function:
const Popup = (props) => {
const [open, setOpen] = React.useState(false);
const toggle = () => {
setOpen(!open);
};
return (
<div>
<Button onClick={() => toggle()}>Click Me</Button>
<Modal className='successModal text-center' open={open} centered={true} size='lg'>
<ModalHeader>{props.title}</ModalHeader>
<ModalBody>
<div>
<img src={successIcon} alt='Success Icon' />
<div className='container my-4'>
<h4>{props.body}</h4>
</div>
</div>
<Button onClick={() => toggle()}>Close</Button>
</ModalBody>
</Modal>
</div>
);
};
export default Popup;
With vanilla/traditional functions:
const Popup = (props) => {
const [open, setOpen] = React.useState(false);
function toggle() {
setOpen(!open);
};
return (
<div>
<Button onClick={toggle}>Click Me</Button>
<Modal className='successModal text-center' open={open} centered={true} size='lg'>
<ModalHeader>{props.title}</ModalHeader>
<ModalBody>
<div>
<img src={successIcon} alt='Success Icon' />
<div className='container my-4'>
<h4>{props.body}</h4>
</div>
</div>
<Button onClick={toggle}>Close</Button>
</ModalBody>
</Modal>
</div>
);
};
export default Popup;

After clicking button component should render EditChannel Component should render

After clicking Button EditChannel component should render. Right now if I before clicking on button EditChannel re-rendering as many as times as list item. After clicking also happening the same. I only want after clicking on button. Please find below lines for code. After clicking EditChannel component should render
Channel card component
import React, { Fragment } from "react";
import { makeStyles } from "#material-ui/core/styles";
import { Typography, Button } from "#material-ui/core";
import Avatar from "#material-ui/core/Avatar";
import { Link } from "react-router-dom";
import { connect } from "react-redux";
import { channelFollow, channelFetchById } from "../../../redux/action/channel";
import EditChannel from "../../Channels/List/Edit";
const ChannelCard = props => {
const classes = useStyles();
const { channel, channelFetchById } = props;
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const view = (
<div className={classes.card}>
<Link to={`/channels/${channel._id}`} className={classes.link}>
<div className={classes.root}>
<Avatar
alt="Remy Sharp"
src={channel.avatar}
className={classes.bigAvatar}
/>
</div>
<div className={classes.title}>
<Typography
variant="subtitle1"
align="center"
className={classes.text}
>
{channel.channelName}
</Typography>
<Typography variant="body2" align="center">
{channel.introduction}
</Typography>
</div>
</Link>
<Typography variant="body2" align="center" className={classes.text1}>
{channel.follows ? channel.follows.length : 0} followers <br />
Language:{channel.language}
</Typography>
<div className={classes.center}>
<div className={classes.button}>
<div className={classes.buttons}>
<Button
variant="contained"
color="primary"
onClick={() => {
channelFetchById(channel._id);
handleClickOpen();
}}
>
Edit
</Button>
{handleClickOpen ? (
<EditChannel setOpen={setOpen} open={open} />
) : null}
</div>
<div>
<Button color="primary" variant="contained">
Delete
</Button>
</div>
</div>
<br />
</div>
</div>
);
return <Fragment>{view}</Fragment>;
};
export default connect(null, { channelFollow, channelFetchById })(ChannelCard);
I guess instead of handleClickOpen only open will work like this,
{open ? (
<EditChannel setOpen={setOpen} open={open} />
) : null}
Because handleClickOpen is click event not boolean variable

Resources