React is it possible to receive onSubmit event in child component? - javascript

I am building a form and each of the form items (I call Cards) are inside of a child components. My submit button is in the parent component of the form items, and when I click submit, I want to to pass the event handler into the child component, may I know is there a way to do it?
I have some code snippeet here to show you guys what I have done, thanks !
// Parent Component
const card = cardsList.map(el => {
return (
<div className="card-holder" key={decodeHtml(el.question)}>
<Card
id="question1"
question={decodeHtml(el.question)}
answer={el.correct_answer}
incorrectAnswers={el.incorrect_answers}
handleSubmit={handleSubmit}
/>
</div>
)
})
function handleSubmit(event) {
event.preventDefault();
console.log(event)
}
return (
<form className="game" style={style} onSubmit={handleSubmit}>
{card}
<input type="submit" value="Submit" className="submit-btn" />
</form>
)
}
Thank you!
// Child Component
import React from "react"
import {shuffle, decodeHtml} from "/src/script/Helper.js"
function Card(props) {
const [arr, setArr] = React.useState(shuffle([0,1,2,3]));
const [checked, setChecked] = React.useState(false)
function toggleCheck(event) {
setChecked(prevChecked => {
return event.target.id
})
}
return (
<div className="card">
<h4 className="card--question">{props.question}</h4>
<div className="card--answers">
<div className="card--answer">
<input
type="radio"
name={props.question}
id={`${props.question} ans1`}
onChange={toggleCheck}
checked={checked === `${props.question} ans1`}
correct={arr[0] === 3 ? "true" : "false"}
/>
<label htmlFor={`${props.question} ans1` }>{arr[0] === 3 ? decodeHtml(props.answer) : decodeHtml(props.incorrectAnswers[arr[0]])}</label>
</div>
<div className="card--answer">
<input
type="radio"
name={props.question}
id={`${props.question} ans2`}
onChange={toggleCheck}
checked={checked === `${props.question} ans2`}
correct={arr[1] === 3 ? "true" : "false"}
/>
<label htmlFor={`${props.question} ans2`}>{arr[1] === 3 ? decodeHtml(props.answer) : decodeHtml(props.incorrectAnswers[arr[1]])}</label>
</div>
<div className="card--answer">
<input
type="radio"
name={props.question}
id={`${props.question} ans3`}
onChange={toggleCheck}
checked={checked === `${props.question} ans3`}
correct={arr[2] === 3 ? "true" : "false"}
/>
<label htmlFor={`${props.question} ans3`}>{arr[2] === 3 ? decodeHtml(props.answer) : decodeHtml(props.incorrectAnswers[arr[2]])}</label>
</div>
<div className="card--answer">
<input
type="radio"
name={props.question}
id={`${props.question} ans4`}
onChange={toggleCheck}
checked={checked === `${props.question} ans4`}
correct={arr[3] === 3 ? "true" : "false"}
/>
<label htmlFor={`${props.question} ans4`}>{arr[3] === 3 ? decodeHtml(props.answer) : decodeHtml(props.incorrectAnswers[arr[3]])}</label>
</div>
</div>
</div>
)
}
export default Card;

Related

Conditional rendering a component with using Radio button

New Tasks.js
import React, { useState } from "react";
import './NewTask.css';
import TaskDetails from "./TaskDetails";
import TaskSetting from "./TaskSetting";
const NewTask = () => {
const [checked, setChecked] = useState("inPerson");
return (
<div className="newtask-div">
<h3 className="header">New Task</h3>
<ul className="task-item">
<li style={{ fontSize: "17px" }}>Select Task Type: </li>
<li>
<input
type="radio"
checked={checked === "inPerson"}
name="inPerson" value="inPerson"
onChange={(e) => {
setChecked(e.target.value)
console.log("show task details")
}}
/>
<lable>In Person</lable>
</li>
<li>
<input
type="radio"
checked={checked === "online"}
name="online" value="online"
onChange={(e) => {
setChecked(e.target.value)
console.log("dont show task detail")
}}
/>
<lable>Online</lable>
</li>
</ul>
<TaskDetails />
</div>
)
}
export default NewTask
I am trying to use the radio button to conditional rendering the Component.
Ex: when clicking the "inPerson" it will render out the "TaskDetail" component and show others component when using "online".
Is there any method to achieve this?
You can render components conditionally based on selected value like below:
...
{
checked === "inPerson" &&
<InpersonComponents />
}
{
checked === "online" &&
<OnlineComponents />
}
...
I recommend this method with conditional rendering
{checked === "inPerson" ? <TaskDetails /> : <SomeOtherComponent />
or
{checked === "inPerson" && <TaskDetails />}
{checked === "online" && <SomeOtherComponent />}
name of inputs should be same if you want to select anyone of them
const NewTask = () => {
const [inPerson, setInPerson] = useState(true);
const [online, setOnline] = useState(false);
return (
<div className="newtask-div">
<h3 className="header">New Task</h3>
<ul className="task-item">
<li style={{ fontSize: "17px" }}>Select Task Type: </li>
<li>
<input
type="radio"
checked={online}
name="singlegroup" value="inPerson"
onChange={(e) => {
setInPerson(e.target.value)
setOnline(!e.target.value)
console.log("show task details")
}}
/>
<lable>In Person</lable>
</li>
<li>
<input
type="radio"
checked={inPerson}
name="singlegroup" value="online"
onChange={(e) => {
setOnline(e.target.value)
setInPerson(!e.target.value)
console.log("dont show task detail")
}}
/>
<lable>Online</lable>
</li>
</ul>
<TaskDetails />
{
online && <Component1/>
}
{
inPerson && <Component2/>
}
</div>
)
}
export default NewTask
Try something along the lines of
{checked !== 'inPerson' && (
<TaskDetails />
)}
Or if you want to make it modularized
const showDetails = () => {
return checked !== 'inPerson'
}
return (
...
{showDetails() && (
<TaskDetails />
)}
...
)

Reactjs problems with radio buttons when fetch data

I want to build menu for my website with food cards. I fetch data(name of food, recipe, price) from my rest api and then i show this data on my react app. In this food card I have three radio buttons for mini, middle and maxi prices. When I change button on one card it changes on all cards. First image, when price 35 and
Second image, when I change price on first card, but it changes on all cards
this is my code:
constructor(props){
super(props);
this.state = {
shavermas : [],
price : ''
};
}
componentDidMount(){
this.findAllPosts();
}
findAllPosts(){
axios.get("http://localhost:8080/api/shaverma/all")
.then(response => response.data)
.then((data) => {
this.setState({shavermas: data})
});
}
onChange = e =>{
this.setState({price : e.target.value})
}
render(){
let {price} = this.state;
const {shavermas} = this.state;
return(
<>
{shavermas.map((shaverma, index) => (
<div className="food-cart">
<div className="product-img-div">
<img
src={shavermaPhoto}
className="d-inline-block product-img"
alt="shaverma"
/>
</div>
<div className="food-cart-body">
<div>
<h3>Шаверма <span>{shaverma.foodName}</span></h3>
<p>{shaverma.recipe}</p>
<form className="radio-buttons">
<div className="radio">
<label className="btn-radio">
<input type="radio" value={shaverma.priceMini} onChange={this.onChange} checked={price.charAt(0) == '' ? shaverma.priceMini : price == shaverma.priceMini}/>
<span>Mini</span>
</label>
</div>
<div className="radio">
<label className="btn-radio">
<input type="radio" value={shaverma.priceMiddle} onChange={this.onChange} checked={price == shaverma.priceMiddle}/>
<span>Middle</span>
</label>
</div>
<div className="radio">
<label className="btn-radio">
<input type="radio" value={shaverma.priceMaxi} onChange={this.onChange} checked={price == shaverma.priceMaxi} />
<span>Maxi</span>
</label>
</div>
</form>
<div className="food-cart-footer">
<strong>{price.charAt(0) === '' ? shaverma.priceMini : price}₴</strong>
<p>Хочу!</p>
</div>
</div>
</div>
</div>
))}
</>
)
}
You are using common Price state for all cards, you have to use price property for individual card,
Use it like this :
onChange = (e,index) =>{
let newShavermas = this.state.shavermas ;
newShavermas[index].price=e.target.value;
this.setState({price : e.target.value})
}
and while fetching the result include price property in each record
findAllPosts(){
axios.get("http://localhost:8080/api/shaverma/all")
.then(response => response.data)
.then((data) => {
let dataVal = data.map(ele=>ele.Price='');
this.setState({shavermas: dataVal })
});
}
and in return call onChange like this :
return(
<>
{shavermas.map((shaverma, index) => (
<div className="food-cart">
<div className="product-img-div">
<img
src={shavermaPhoto}
className="d-inline-block product-img"
alt="shaverma"
/>
</div>
<div className="food-cart-body">
<div>
<h3>Шаверма <span>{shaverma.foodName}</span></h3>
<p>{shaverma.recipe}</p>
<form className="radio-buttons">
<div className="radio">
<label className="btn-radio">
<input type="radio" value={shaverma.priceMini} onChange={(e)=>this.onChange(e,index)} checked={shaverma.price.charAt(0) == '' ? shaverma.priceMini : price == shaverma.priceMini}/>
<span>Mini</span>
</label>
</div>
<div className="radio">
<label className="btn-radio">
<input type="radio" value={shaverma.priceMiddle} onChange={(e)=>this.onChange(e,index)} checked={shaverma.price == shaverma.priceMiddle}/>
<span>Middle</span>
</label>
</div>
<div className="radio">
<label className="btn-radio">
<input type="radio" value={shaverma.priceMaxi} onChange={(e)=>this.onChange(e,index)} checked={shaverma.price == shaverma.priceMaxi} />
<span>Maxi</span>
</label>
</div>
</form>
<div className="food-cart-footer">
<strong>{shaverma.price.charAt(0) === '' ? shaverma.priceMini : shaverma.price}₴</strong>
<p>Хочу!</p>
</div>
</div>
</div>
</div>
))}
</>
)
This is because all of you cart items are looking at the same state value!
onChange = e =>{
this.setState({price : e.target.value}) < --- changes price to all cards
}
To solve this, you will need to have a price inside each shaverma then change it alone.
I would suggest starting by creating a FootCart component with its own state.
Something along the line of:
class FootCart implements React.Component {
...
render() {
return (
<div className="food-cart">
...
</div>
}
}
class Cards implements React.Component {
...
render(){
return (
<>
{shavermas.map((shaverma, index) => (<FootCart props/>)}
</>
}
}
Good Luck!

ReactJS TypeError: Cannot read property 'setState' of undefined even after binding in the constructor and also using the arrow function

I'm getting the error at line 116
checked={this.setState({selectedOption: "Male"})}
If I remove that line, then I get the same error at the next line.
import React, { Component } from "react";
import { Document, Page } from "react-pdf";
import { pdfjs } from 'react-pdf';
import SplitPane, { Pane } from 'react-split-pane';
import { Button } from 'react-bootstrap';
import axios from 'axios';
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
export class TestPage extends React.Component{
constructor(props){
super(props);
this.state = {
numPages: null,
pageNumber: 1,
items: [],
responses: [],
color: '',
name: "React",
selectedOption: "Male",
};
this.onValueChange = this.onValueChange.bind(this);
this.formSubmit = this.formSubmit.bind(this);
this.goToPrevPage = this.goToPrevPage.bind(this);
this.goToNextPage = this.goToNextPage.bind(this);
this.onDocumentLoadSuccess = this.onDocumentLoadSuccess.bind(this);
}
componentDidMount(){
axios.get("http://localhost:5000/getquestiondata")
.then(
(result) => {
this.setState({
items: result.data.number,
});
}).catch(error => {
console.log("Error: ", error)
})
}
onDocumentLoadSuccess = ({ numPages }) => {
this.setState({ numPages: numPages });
};
formSubmit = event => {
event.preventDefault();
console.log(this.state.selectedOption);
}
onValueChange = event =>{
this.setState({
selectedOption: event.target.value
});
}
goToPrevPage = () =>
this.setState(state => ({ pageNumber: state.pageNumber - 1 }));
goToNextPage = () =>
this.setState(state => ({ pageNumber: state.pageNumber + 1 }));
render() {
const { pageNumber, numPages, items } = this.state;
var quesNos = [];
// var resp = [];
for(var i = 0; i < items; i++){
quesNos.push(i);
// resp.push(i);
}
console.log("QuesNos: ",quesNos);
return (
<div>
<h1 style={{textAlign: "center"}}>Online Test #1</h1>
<hr />
<SplitPane
split="vertical"
sizes={[50,50]}
defaultSize={parseInt(localStorage.getItem('splitPos'), 10)}
onChange={size => localStorage.setItem('splitPos', size)}
style={{overflow: 'scroll'}}
>
<div style={{overflowY: 'scroll', height:'100%', overflowX: 'scroll'}}>
<nav>
<button onClick={this.goToPrevPage}>Prev</button>
<button onClick={this.goToNextPage}>Next</button>
</nav>
<div>
<Document
file={require('./sample.pdf')}
onLoadSuccess={this.onDocumentLoadSuccess}
>
<Page pageNumber={pageNumber}/>
</Document>
</div>
<p>
Page {pageNumber} of {numPages}
</p>
</div>
<div style={{overflowY: 'scroll', height:'100%', overflowX: 'scroll'}}>
<form onSubmit={this.answerSubmit}>
{quesNos.map(function(qno){
return(
<div>
<div className="radio">
<label>
<input
type="radio"
value="Male"
checked={this.setState({selectedOption: "Male"})}
onChange={this.onValueChange}
/>
Male
</label>
</div>
<div className="radio">
<label>
<input
type="radio"
value="Female"
checked={this.state.selectedOption === "Female"}
onChange={this.onValueChange}
/>
Female
</label>
</div>
<div className="radio">
<label>
<input
type="radio"
value="Other"
checked={this.state.selectedOption === "Other"}
onChange={this.onValueChange}
/>
Other
</label>
</div>
<div>
Selected option is : {this.state.selectedOption}
</div>
<Button variant="btn btn-primary" type="submit">Submit</Button>
</div>
);
})}
<Button variant='primary' type="submit">Submit</Button>{' '}
</form>
</div>
</SplitPane>
</div>
);
}
}
Any idea why this error happens and how to fix it?
Thanks!
Two errors in your code
Your map function must be an arrow function to leverage this of the class/ or you use .bind for the map function
You must not setState directly in render. I assume you meant to compare state with checked attribute like checked={this.state.selectedOption === "Male"}
Updated code below
{quesNos.map((qno) => { // arrow function here
return(
<div>
<div className="radio">
<label>
<input
type="radio"
value="Male"
checked={this.state.selectedOption === "Male"} // Comparison here
onChange={this.onValueChange}
/>
Male
</label>
</div>
<div className="radio">
<label>
<input
type="radio"
value="Female"
checked={this.state.selectedOption === "Female"}
onChange={this.onValueChange}
/>
Female
</label>
</div>
<div className="radio">
<label>
<input
type="radio"
value="Other"
checked={this.state.selectedOption === "Other"}
onChange={this.onValueChange}
/>
Other
</label>
</div>
<div>
Selected option is : {this.state.selectedOption}
</div>
<Button variant="btn btn-primary" type="submit">Submit</Button>
</div>
);
})}

How to keep track of selections in array of radio button groups?

I'm baffled over this problem that seems to have a simple solution right under my nose, but I can't find it.
I'm looping 42 groups of radio buttons, and I'm only as yet able to get one (out of 42 * 4 buttons) to be selected. I render the first statement, and each statement has 4 choices... Thank you so much for helping.
import React, { Component } from 'react'
class Acme extends Component {
constructor(props) {
super(props);
this.state = {
selections: [],
statements: "forty-two statements+separated by add signs".split('+')
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
// lost here -- ???
handleChange(event) {
this.state.selections.push( event.target.value )
}
handleSubmit(event) {
event.preventDefault()
alert("Hello")
}
render() {
return (
<div className="pure-form">
<h2>Acme</h2>
<hr />
<h3>
Please read each statement and select a number 0, 1, 2 or 3 which indicates how much the statement applied to you <b>over the past week</b>. There are no right or wrong answers. Do not spend too much time on any statement.
</h3>
<form onSubmit={this.handleSubmit}>
{
this.state.statements.map(
(statement, index) => (
<div className="pure-g">
<div className="pure-u-1 pure-u-md-21-24 pure-control-group">
<h4>{index+1}. {statement}</h4>
<div className="pure-u-5-24">
<label className="pure-radio">
<input type="radio" value={0} key={index}
checked={this.state.selections[index] === 0 }
onChange={this.handleChange} />
0
</label>
</div>
<div className="radio pure-u-5-24">
<label className="pure-radio">
<input type="radio" value={1} key={index}
checked={this.state.selections[index] === 1}
onChange={this.handleChange } />
1
</label>
</div>
<div className="radio pure-u-5-24">
<label className="pure-radio">
<input type="radio" value={2} key={index}
checked={this.state.selections[index] === 2 }
onChange={this.handleChange } />
2
</label>
</div>
<div className="radio pure-u-5-24">
<label className="pure-radio">
<input type="radio" value={3} key={index}
checked={this.state.selections[index] === 3 }
onChange={this.handleChange } />
3
</label>
</div>
</div>
</div>
)
)
}
<button type="submit" className="pure-button pure-button-primary">
See Results
</button>
</form>
</div>
)
}
}
export default Acme
You need to keep a map of selections with the key as the statement id. I have attached the sample code
class Acme extends React.Component {
constructor(props) {
super(props);
this.state = {
selections: {},
statements: "forty-two statements+separated by add signs".split('+')
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange(event) {
const [id, value] = event.target.value.split('-');
this.setState({
selections: {
...this.state.selections,
[id]: parseInt(value),
}
});
}
handleSubmit(event) {
event.preventDefault()
alert("Hello")
}
render() {
return (
<div className="pure-form">
<h2>Acme</h2>
<hr />
<h3>
Please read each statement and select a number 0, 1, 2 or 3 which indicates how much the statement applied to you <b>over the past week</b>. There are no right or wrong answers. Do not spend too much time on any statement.
</h3>
<form onSubmit={this.handleSubmit}>
{
this.state.statements.map(
(statement, index) => (
<div className="pure-g" key={index}>
<div className="pure-u-1 pure-u-md-21-24 pure-control-group">
<h4>{index+1}. {statement}</h4>
<div className="pure-u-5-24">
<label className="pure-radio">
<input type="radio" value={`${index}-0`} key={`${index}-0`}
checked={this.state.selections[index] === 0 }
onChange={this.handleChange} />
0
</label>
</div>
<div className="radio pure-u-5-24">
<label className="pure-radio">
<input type="radio" value={`${index}-1`} key={`${index}-1`}
checked={this.state.selections[index] === 1}
onChange={this.handleChange } />
1
</label>
</div>
<div className="radio pure-u-5-24">
<label className="pure-radio">
<input type="radio" value={`${index}-2`} key={`${index}-2`}
checked={this.state.selections[index] === 2 }
onChange={this.handleChange } />
2
</label>
</div>
<div className="radio pure-u-5-24">
<label className="pure-radio">
<input type="radio" value={`${index}-3`} key={`${index}-3`}
checked={this.state.selections[index] === 3 }
onChange={this.handleChange } />
3
</label>
</div>
</div>
</div>
)
)
}
<button type="submit" className="pure-button pure-button-primary">
See Results
</button>
</form>
</div>
)
}
}
ReactDOM.render(
<Acme />,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>

How to check/uncheck a list of checkboxes in react

I have a room page and in that page I have a list of sensors attached to that room, those sensors can be selected using a checkbox, like so:
<div className="checkboxRowContent">
{sensors.map(s => {
return (
<div className="checkboxElementWrapper" key={s.id}>
<label htmlFor={`sensor${s.id}`}>
<div className="checkboxLabel">
<Link to={`/sensors/edit/${s.id}`}>{s.name}</Link>
</div>
<input
type="checkbox"
id={`sensor${s.id}`}
name="sensorId"
value={s.id}
checked={s.roomId === values.id}
onChange={handleCheckbox}
/>
<span className="checkbox" />
</label>
</div>
);
})}
</div>
the problem is - this approach prohibits me from unchecking the checkbox (so if in db that sensor is attached to that room - that's it). How could I rewrite this so that I can check/uncheck this checkbox?
in the class you must have state for that,
a sample would be somewhat like this
export default class yourComponent extends React.Component {
state = {
checkedBoxes: []
}
handleCheckbox = (e, s) => {
const checkedBoxes = [...this.state.checkedBoxes];
if(e.target.checked) {
checkedBoxes.push(s)
} else {
const index = checkedBoxes.findIndex((ch) => ch.roomId === s.roomId);
checkedBoxes.splice(index, 1);
}
this.setState({checkedBoxes});
}
render() {
return(
<div className="checkboxRowContent">
{sensors.map(s => {
return (
<div className="checkboxElementWrapper" key={s.id}>
<label htmlFor={`sensor${s.id}`}>
<div className="checkboxLabel">
<Link to={`/sensors/edit/${s.id}`}>{s.name}</Link>
</div>
<input
type="checkbox"
id={`sensor${s.id}`}
name="sensorId"
checked={checkedBoxes.find((ch) => ch.roomId === s.roomId)}
onChange={(e) => handleCheckbox(e, s)}
/>
<span className="checkbox" />
</label>
</div>
);
})}
</div>
)
}
}
A state, checkedBoxes for getting all selected checkboxes.
A handler handleCheckbox for handling checkbox clicks,
You have handleCheckBox and a controlled component. We don't see what you do in the event handler but when it's controlled, you can check it by altering your sensors array (if in state/props) so s.roomId === values.id will be true.
If you don't want it to be controlled, you can probably use defaultChecked which will let you work with it in a different way.
see https://reactjs.org/docs/forms.html#controlled-components
import React, {Component} from 'react';
import axios from 'axios';
const Books = props=>(
<div className='form-group'>
<label>{props.book}
<input type='checkbox' name={props.name} className='form-check' onChange={props.onChange} />
</label>
</div>
)
class Total extends Component{
constructor(props){
super(props);
this.onChangeCheck = this.onChangeCheck.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state={
checkBoxes: [],
books:[]
}
}
componentDidMount() {
axios.get('http://localhost:3000/api/book/').then(resolve=>{
console.log(resolve.data.data);
this.setState({
books:resolve.data.data
}).catch(err=>{
console.log(err)
})
})
}
onChangeCheck(e){
console.log(e.target.name)
if(e.target.checked){
const array = this.state.checkBoxes;
array.push(e.target.name)
this.setState({
checkBoxes:array
})
}else{
const array = this.state.checkBoxes;
const index = array.indexOf(e.target.name);
console.log(index)
array.splice(index,1);
console.log(array);
this.setState({
checkBoxes:array
})
}
}
onSubmit(e){
e.preventDefault();
axios.put("http://localhost:8080/books/getTotal/",this.state.checkBoxes).then(resolve=>{
console.log(resolve)
alert(`Total price of books ${resolve.data}`);
}).catch(err=>{
console.log(err);
})
}
render(){
return(
<div className='card'>
<div className='card-header'>
</div>
<div className='card-body'>
<form className='form' onSubmit={this.onSubmit}>
<div className='form-group'>
{
this.state.books.map(object=>(
<Books name={object._id} book={object.name} onChange={this.onChangeCheck} />
)
)
}
</div>
<div className='form-group'>
<button type='submit' className='btn btn-success'>Get Total</button>
</div>
</form>
</div>
</div>
)
}
}
export default Total;

Categories

Resources