I know there are question asked before, but I have tried the solution but it's not working. I have split the component differently but then also it refresh on every single keypress.
const TenementRegistration = () => {
const [show, setShow] = useState(false);
const [name, setName] = useState("");
const [editId, setEditId] = useState("");
function Example() {
const onSubmitHandler = async () => {
const data = {
name: name
}
await services.postService("User", data).then((res) => {
onGetUserData();
});
}
return(
<Modal
show={show}
onHide={() => setShow(false)}
size="lg"
aria-labelledby="example-custom-modal-styling-title"
scrollable="true"
centered
animation="true"
>
<Modal.Header closeButton>
<Modal.Title id="example-custom-modal-styling-title">
Add User
</Modal.Title>
</Modal.Header>
<Modal.Body>
<div className="form-container">
<form>
<Row>
<div className="form-group col-12 col-md-6
center">
<label for="inputName" className="asy-
FormLabel">
Name
</label>
<input
type="text"
className="form-control asy-InputValues"
id="policyName"
placeholder="Enter Property Id"
onChange={(e) => {
setName(e.target.value);
}}
value={name}
required
/>
</div>
</Row>
</form>
</div>
</Modal.Body>
<Modal.Footer>
<button
type="button"
className="submit-button"
onClick={() => {
onSubmitHandler();
}}
>
Submit
</button>
</Modal.Footer>
</Modal>
const [data, setData] = useState([]);
useEffect(() => {
onGetUserData();
}, []);
const onGetUserData = async () => {
services.getService("User").then((res) => {
setData(res.data);
});
};
const onEditData = async (id) => {
setShow(true);
const newData = data.filter((obj) => obj.id === id)[0];
setName(newData.name);
}
//Table where we show name and pass id to update button
}
I have also tried to Split the Modal and separate the form (not in this example) but it didn't work any suggestions how to handle the modal problem
Try this
In your button onClick
<button
type="button"
className="submit-button"
onClick={(event) =>
{
onSubmitHandler(event);
}}
>
Submit
</button>
then in that function:
const onSubmitHandler = async (event) => {
event.preventDefault()
const data = {
name: name
}
await services.postService("User", data).then((res) => {
onGetUserData();
});
}
Related
I am struggling to understand how to use React modals (specifically React-Bootstrap) when asynchronous code is involved. I am learning about promises and React at the same time so I hope I am formulating the question correctly.
My goal is to launch an asynchronus function with parameters provided by a modal. This is what I am doing at the moment, it works, but I have the impression that I am not separating concerns.
When a button is clicked the async function simulateImprovedBuilding is launched.
async function simulateImprovedBuilding() {
function selectOsMeasures(measures) {
let selectedOsMeasures = measures;
let scenario = "Improved";
simulateBuilding(scenario, selectedOsMeasures).then((result) => {
console.log("Finally!!!!");
renderMonthlySimulation(result);
});
}
const osMeasuresModalRoot = createRoot(document.getElementById("react_modal"));
osMeasuresModalRoot.render(<OsMeasuresModal action={selectOsMeasures} />);
console.log("Done");
}
The modal is rendered, I make the selection and the component execute selectOsMeasures to update the parameters, launch the long running async process and, when completed renders the results on a chart. This is the component
function osMeasuresModalRoot(props) {
const [show, setShow] = useState(true);
const handleClose = () => {
setShow(false);
let selectedOsMeasures = osMeasures.filter(x => selections[osMeasures.indexOf(x)]);
console.log(selectedOsMeasures);
props.action(selectedOsMeasures);
};
const handleShow = () => setShow(true); // Not used
const [selections, setSelections] = useState([]);
const [osMeasures, setOsMeaaures] = useState([]);
useEffect(() => {
let url = `http://${osServer}/os_measures`;
fetch(url)
.then((response) => response.json())
.then((result) => {
setOsMeaaures(result);
setSelections(Array(result.length).fill(false));
});
}, []);
return (
<>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Select ECMs</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
{osMeasures.map((measure, index) => (
<div key={`${measure}`} className="mb-3">
<Form.Check
type={"checkbox"}
id={`${measure}`}
label={`${measure}`}
onChange={() => {
selections[index] = !selections[index];
console.log(selections);
}}
/>
</div>
))}
</Form>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Cancel
</Button>
<Button variant="primary" onClick={handleClose}>
Simulate
</Button>
</Modal.Footer>
</Modal>
</>
);
}
I think the selectOsMeasures function is doing too much. I think I should do something like:
simulateImprovedBuilding()
.then(result => renderMonthlySimulation(result));
But if I do this, the function exits immediately after the React render function and of course result is undefined.
First of all, is my concern valid? If so, how should I refactor my code?
How about increasing the scope of the function and making the result global? It's a bit hacky but is more along the lines of what React should be doing rather than having multiple rendering functions. Then it will automatically switch between the Modal and the graph rendering function when it changes:
function SimulateImprovedBuilding() {
let r_result = undefined;
function OsMeasuresModal(props) {
const [show, setShow] = useState(true);
const handleClose = () => {
setShow(false);
let selectedOsMeasures = osMeasures.filter(x => selections[osMeasures.indexOf(x)]);
console.log(selectedOsMeasures);
props.action(selectedOsMeasures);
};
const handleShow = () => setShow(true); // Not used
const [selections, setSelections] = useState([]);
const [osMeasures, setOsMeaaures] = useState([]);
useEffect(() => {
let url = `http://${osServer}/os_measures`;
fetch(url)
.then((response) => response.json())
.then((result) => {
setOsMeaaures(result);
setSelections(Array(result.length).fill(false));
});
}, []);
return (
<>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Select ECMs</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
{osMeasures.map((measure, index) => (
<div key={`${measure}`} className="mb-3">
<Form.Check
type={"checkbox"}
id={`${measure}`}
label={`${measure}`}
onChange={() => {
selections[index] = !selections[index];
console.log(selections);
}}
/>
</div>
))}
</Form>
</Modal.Body>
<Modal.Footer>
<button onClick={handleClose}>
Cancel
</button>
<button onClick={handleClose}>
Simulate
</button>
</Modal.Footer>
</Modal>
</>
);
}
function selectOsMeasures(measures) {
let selectedOsMeasures = measures;
let scenario = "Improved";
simulateBuilding(scenario, selectedOsMeasures).then(r => {
console.log("Finally!!!!");
r_result = r;
});
}
function Render() {
return (
<>
{r_result===undefined? <OsMeasuresModal action={selectOsMeasures} /> : renderMonthlySimulation(r_result)}
</>
)
}
const osMeasuresModalRoot = createRoot(document.getElementById("react_modal"));
osMeasuresModalRoot.render(<Render/>);
console.log("Done");
}
Note: the renderMonthlySimulation(r_result) function will need to become a react component that returns what you want to render
Same can be done in multiple ways, you could use a 'Context' across all the react functions, or use 'Routing' to switch between things you're rendering. It's much better to have a singular .render.
I have a modal that pops up on a dashboard if a condition is true and renders a checkbox. I can't seem to toggle to Modal off on the onClick function. Here is an example of the code.
Dashboard
const conditionalAgreement = false;
<Modal showModal={showModal} conditionalAgreement={conditionalAgreement} />
Modal
const Modal = ({ conditionalAgreement }) => {
const [showModal, setShowModal] = useState(false);
const [checkboxCondition, setCheckboxCondition = useState(false);
useEffect(() => {
if (conditionalAgreement) {
setShowModal(true);
}
}, [conditionalAgreement]);
const OnChangeHandler = () => {
setCheckboxCondition(!setCheckboxCondition);
};
const OnClickHandler = () => {
setShowModal(false);
};
return (
<div className={css.modal}>
<div className={css.checkbox}>
<CheckboxComponent
value={checkboxCondition}
onChange={OnChangeHandler}
description={tick this box"}
/>
</div>
<div className={css.buttonContainer}>
<ButtonComponent
onClick={OnClickHandler}
>
Save
</ButtonComponent>
</div>
</div>
);
};
export default Modal;
Dashboard:
const Dashboard = () => {
const [showModal, setShowModal] = useState(false);
return (
{showModal && (
<Modal showModal={showModal} closeModal={() => setShowModal(false)} />
)}
)
}
Modal:
const Modal = ({ showModal, closeModal }) => {
const [checkboxCondition, setCheckboxCondition] = useState(false);
const onChangeHandler = () => {
setCheckboxCondition(!checkboxCondition);
};
const onClickHandler = () => {
closeModal();
};
return (
<div className={css.modal}>
<div className={css.checkbox}>
<CheckboxComponent
value={checkboxCondition}
onChange={onChangeHandler}
description={tick this box"}
/>
</div>
<div className={css.buttonContainer}>
<ButtonComponent
onClick={onClickHandler}
>
Save
</ButtonComponent>
</div>
</div>
);
};
Now, as mention by #RobinZigmond something in your Dashboard component should set showModal to true so that your Modal appears.
I just want to preface this that I am learning JavaScript and React so this is all very new to me.
I am building a "simple" movie rating app and need to be able to push a review to a div "on submit" and cannot figure out how to do so. I have tried using update state in react and/or creating functions to try to accomplish this and cannot figure out how to do this for the life of me. I did somewhat succeed using the latter method, but was getting errors about using unique key props. The other problem was I am to use a star-rating component and when I submitted the review, it wasn't pushing that to the div. This is where I'm at currently:
import { Button, Form, Input } from "reactstrap";
import Stars from "./stars";
export default function ReviewForm() {
const [reviews, setReviews] = useState("");
const onChange = (e: any) => {
setReviews(e.target.value);
};
const onSubmit = (e: any) => {
console.log("Form Submitted");
};
return (
<div className="form-container">
<Stars />
<Form onSubmit={onSubmit}>
<Input
className="form-control" type="text"
placeholder="Enter you review"
value={reviews}
onChange={onChange}
/>
<br></br>
<Button type="submit" className="btn btn-primary">
Submit
</Button>
</Form>
</div>
);
}
// This is what I have in my Stars component:
import React, { useState } from "react";
import { FaStar} from 'react-icons/fa'
const Stars = () => {
const [rating, setRating] = useState(0);
const [hover, setHover] = useState(null);
return(
<div>
{[...Array(5)].map((star, i) => {
const ratingValue = i + 1;
return <label>
<input
type="radio"
name="rating"
value={ratingValue}
onClick={() => setRating(ratingValue)}
/>
<FaStar
className="star"
color={ratingValue <= (hover || rating) ? "gold" : "lightgray"}
size={20}
onMouseEnter={() => setHover(ratingValue)}
onMouseLeave={() => setHover(null)}
/>
</label>;
})}
<p>I rate this movie {rating + " stars"}</p>
</div>
);
};
export default Stars```
Here is the working version of your code. You should use key in your map and e.preventDefault() in your form submit function. As final touch you should set another state inside your form submit and show this value in a div or some html element. Also I see that you want to get child state into parent so you can call callback for this https://codesandbox.io/embed/brave-euler-ybp9cx?fontsize=14&hidenavigation=1&theme=dark
ReviewForm.js
export default function ReviewForm() {
const [reviews, setReviews] = useState("");
const [value, setValue] = useState("");
const [star, setStar] = useState();
const onChange = (e: any) => {
setReviews(e.target.value);
};
const onSubmit = (e: any) => {
e.preventDefault();
setValue(reviews + " with " + star + " star ");
};
return (
<div className="form-container">
<Stars setStar={setStar} />
<Form onSubmit={onSubmit}>
<Input
className="form-control"
type="text"
placeholder="Enter you review"
value={reviews}
onChange={onChange}
/>
<br></br>
<Button type="submit" className="btn btn-primary">
Submit
</Button>
<div>{value}</div>
</Form>
</div>
);
}
Stars.js
const Stars = ({ setStar }) => {
const [rating, setRating] = useState(0);
const [hover, setHover] = useState(null);
const handleClick = (ratingValue) => {
setRating(ratingValue);
setStar(ratingValue);
};
return (
<div>
{[...Array(5)].map((star, i) => {
const ratingValue = i + 1;
return (
<label key={i}>
<input
type="radio"
name="rating"
value={ratingValue}
onClick={() => handleClick(ratingValue)}
/>
<FaStar
className="star"
color={ratingValue <= (hover || rating) ? "gold" : "lightgray"}
size={20}
onMouseEnter={() => setHover(ratingValue)}
onMouseLeave={() => setHover(null)}
/>
</label>
);
})}
<p>I rate this movie {rating + " stars"}</p>
</div>
);
};
export default Stars;
You probably are seeing a page refresh when you press the submit button. This is the default behavior of HTML forms.
When using React or any front-end framework, you'd want to handle the form submission yourself rather than letting the browser submit your forms.
In your onSubmit function, add the following line
e.preventDefult()
const onSubmit = (e: any) => {
e.preventDefault()
console.log("Form Submitted");
};
Your code will work perfectly.
import { Button, Form, Input } from "reactstrap";
import Stars from "./stars";
export default function ReviewForm() {
const [Reviews, setReviews] = useState("");
const [ReviewsRating, setReviewsRating] = useState(5);
const [Reviews_, setReviews_] = useState([]);
const onChange = (e: any) => {
setReviews(e.target.value);
};
const onSubmit = (e: any) => {
e.preventDefault()
console.log("Form Submitted");
//After upload to the server
setReviews_([Reviews, ...Reviews_]
};
return (
<div className="form-container">
<Stars getRating={getRating}/>
<Form onSubmit={onSubmit}>
<Input
className="form-control" type="text"
placeholder="Enter you review"
value={reviews}
onChange={onChange}
/>
<br></br>
<Button type="submit" className="btn btn-primary">
Submit
</Button>
</Form>
<div class="reviews">
{Reviews_.map(item => <div> {item}</div> )}
</>
</div>
);
}```
Then to get the stars rating value use props like...
And make sure you call that property (function) inside your Starts component
const getRating =(value)=>{
setReviewsRating(value)
}
I want to scroll to the bottom of the div whenever a new message is sent in the Message. I can do this with jQuery but wondering what the best approach in React is.
This is the Messages.js component I am in. Thank you in advance!
const Messages = (props) => {
const [inputMessage, setInputMessage] = useState('');
const handleChange = (event) => {
setInputMessage(event.target.value);
}
const handleSubmit = (event, message) => {
event.preventDefault();
props.setRoomMessages([...props.roomMessages, {id: Date.now(), name: props.username, message: inputMessage}]);
setInputMessage('');
}
const messages = props.roomMessages.map((message, index) => {
return (
<Message
key={index}
name={message?.name}
message={message?.message}
/>
)
})
return (
<>
<div className="Messages">
{messages}
</div>
<form className="chat-input form-inline" onSubmit={handleSubmit}>
<div className="form-group">
<input
className="form-control"
type="text"
value={inputMessage}
placeholder="Type a message..."
onChange={handleChange}
/>
<button className="btn btn-link">Send</button>
</div>
</form>
</>
)
}
export default Messages;
Try this, one way to do it is to use ref
import { useState, useRef } from 'react';
const Messages = props => {
const [inputMessage, setInputMessage] = useState('');
const myMessage = useRef(null);
const handleChange = event => {
setInputMessage(event.target.value);
};
const handleSubmit = (event, message) => {
event.preventDefault();
props.setRoomMessages([...props.roomMessages, { id: Date.now(), name: props.username, message: inputMessage }]);
setInputMessage('');
// HERE IS THE NEW CODE
if (myMessage && myMessage.current) {
myMessage.current.scrollTop = myMessage.current.scrollHeight;
}
};
const messages = props.roomMessages.map((message, index) => {
return <Message key={index} name={message?.name} message={message?.message} />;
});
return (
<>
<div className="Messages" ref={myMessage}>
{messages}
</div>
<form className="chat-input form-inline" onSubmit={handleSubmit}>
<div className="form-group">
<input
className="form-control"
type="text"
value={inputMessage}
placeholder="Type a message..."
onChange={handleChange}
/>
<button className="btn btn-link">Send</button>
</div>
</form>
</>
);
};
export default Messages;
Here is my Login component:
const Login = () => {
const [user, setUser] = useState("");
const [pass, setPass] = useState("");
return (
<div>
<p>Login</p>
<input
type="text"
onChange={(e) => {
setUser(e.target.value);
}}
/>
<input
type="password"
onChange={(e) => {
setPass(e.target.value);
}}
/>
<button onClick={submit(user, pass)}>
Submit
</button>
</div>
);
};
It renders on my webpage, but it calls the submit() function whenever I input to these two: text and password. Looking at my code, I've only set the onClick to call the submit function.
Is there something wrong with my code?
EDIT: Removed classNames for easier viewing
You are calling the submit function on every render. onClick takes a function, but you are directly calling a function.
<button onClick={submit(user, pass)}>
Submit
</button>
will be replaced by
<button onClick={()=>submit(user, pass)}>
Submit
</button>
try :
const Login = () => {
const [user, setUser] = useState("");
const [pass, setPass] = useState("");
const onSubmit = () => {
submit(user,pass)
}
return (
<div>
<p>Login</p>
<input
type="text"
onChange={(e) => {
setUser(e.target.value);
}}
/>
<input
type="password"
onChange={(e) => {
setPass(e.target.value);
}}
/>
<button onClick={onSubmit}>
Submit
</button>
</div>
);
};