React send child object into parent array and submit form - javascript

Newbie issues: Building a shopping cart, I have a form in a modal.
The submit button is that of the modal (outside the form tags), but seems to be sending the form to the handleOnSubmit handler. However, apart from closing the modal, I am unable to update the productBought and productName state values.
Ideally, I would like to push the basket[] content (from InputList class) unto a new array in the parent (AsyncAwaitLocalhost), after which I can submit the entire purchase to an API.
refs are deprecated and various other flavors of sharing data are failing me.
My challenges are:
(i) How can I share the push the basket object to a waiting parent state object?
and
(ii) How can I submit the form after console logging the form objects?
Thanks.
AsyncAwaitLocalhost.jsx
import React from 'react'
import "./App.css"
import { Modal, Button } from "react-bootstrap"
import Form from "./Form.jsx"
class AsyncAwaitLocalhost extends React.Component {
constructor(props) {
super(props);
this.state = {
sharedVar: "init",
jsonContent: [],
isOpen: null,
productBought: 0,
productName: "anItem"
}
this.handleOnSubmit = this.handleOnSubmit.bind(this)
}
updateShared(shared_value) {
this.setState({ sharedVar: shared_value });
}
async componentDidMount() {
// GET request using fetch with async/await
const response = await fetch('http://localhost:8080/cakes');
const data = await response.json();
this.setState({ jsonContent: data.cakes, greeting: this.state.greeting, newGreeting: this.state.newGreeting })
}
openModal = (index) => () => this.setState({ isOpen: index });
closeModal = () => this.setState({ isOpen: null });
handleCloseModal() {
this.setState({ isOpen: null });
}
handleOnSubmit(event) {
event.preventDefault()
this.setState({ productBought: event.target.name.value })
this.setState({ productName: event.target.name.value })
console.log("Inside handleOnSubmit: " + this.state.productBought)
this.handleCloseModal();
}
render() {
const { jsonContent } = this.state;
const { sharedVar } = this.state;
const { productBought } = this.state;
const { productName } = this.state;
console.log("shared var in parent: " + sharedVar)
console.log("productBought in parent: " + productBought)
const Cake = ({ name, id, text, image, comment, price, dbutton, dmodal }) => (
<div className="rounded-well">
<div>
<img src={image} width="120px" alt={name} />
</div>
<div>
<p>{name}</p>
<p>{text}</p>
<p>{comment}</p>
<p>{id}</p>
<p>{price}</p>
<div> {this.state.sharedVar}</div>
<div>{dbutton}</div>
<div id="dModal" className="dModalClosed">{dmodal}</div>
</div>
</div>
);
return (
<div className="card text-center m-3">
Showing HTTP Requests, CSS , Data Transfer & Modals
<h5 className="card-header">See Cakes</h5>
<div className="card-body">
<div>
{this.state.jsonContent.map((cake, index) => (
<Cake
image={cake.image}
text={cake.text}
key={index}
price={"£" + cake.price + ".00"}
dbutton={<Button onClick={this.openModal(index)}>See more</Button>}
dmodal={
<Modal id={cake.name} show={this.state.isOpen === index}
onHide={this.closeModal} className="image-container">
<Modal.Header closeButton>
<Modal.Title>{cake.text}</Modal.Title>
</Modal.Header>
<Modal.Body>
<div>
<img src={cake.image} width="300px" alt={cake.name} />
</div>
<br />
<p>{cake.text}</p>
<p>{cake.comment}</p>
<div>
<form id={cake.name} method="POST" action="#" onSubmit={this.handleOnSubmit} >
{<Form dataFromParent={this.state.jsonContent[index]} onChange />}
</form>
</div>
</Modal.Body>
<Modal.Footer>
<Button variant="primary" onClick={this.closeModal}>
Cancel
</Button>
<Button form={cake.name} type="submit" variant="secondary" onClick={this.handleOnSubmit}>
Add to basket
</Button>
</Modal.Footer>
</Modal>
}
/>
))}
<div>
</div>
</div>
</div>
</div>
);
}
}
export { AsyncAwaitLocalhost };
Form.jsx
import React from 'react';
class InputList extends React.Component {
constructor(props) {
super(props)
this.state = {
bought: 0,
sharedVar: "input field",
cakeItems:{},
basket: [],
input: 0,
item: ""
}
}
handleChange(index, e) {
const value = e.target.value
this.props.onChange(index, {...this.props.data[index], buy: value})
this.state.bought = value
this.state.cakeItems = {item: this.props.data[0].name, number: this.state.bought}
this.state.basket.push(this.state.cakeItems)
}
render() {
const { bought } = this.state;
const {cakeItems} = this.state;
const {basket} = this.state;
const { sharedVar } = this.state;
const { item } = this.state;
// this.state.sharedVar = this.props.dataFromParent[0].name
console.log("pre-bought: " + bought);
console.log("Shared var in input class: " + sharedVar);
console.log("basket in InputClass: " + JSON.stringify(basket))
return (
<div>
Buy:
{this.props.data.map((d, i) => <input key="0" value={d.buy} name="productBought" onChange={this.handleChange.bind(this, i)} />)}
{this.props.data.map((d, i) => <input key="1" type="hidden" value={d.name} name="productName" />)}
<>
To buy: {bought}
</>
</div>
)
}
}
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
data: [],
basket: [],
sharedVar: "form field",
usrBasket: {
buy: 1,
id: "randomStr",
items: [{buy: 1, item: ""}],
value_key: "",
nuArry: []
},
input: {}
}
}
loadStuff() {
this.setState({
data: [this.props.dataFromParent]
})
// console.log('got some stuff from the server in Form: ' + JSON.stringify(this.props.dataFromParent))
}
handleChange(index, value) {
console.log('data changed!', value)
const data = [...this.state.data]
data.splice(index, 1, value)
this.setState({
data,
})
this.state.basket = this.state.cakeItems
console.log("basket in FormClass: " + JSON.stringify(this.state.basket))
}
componentDidMount() {
this.loadStuff()
}
render() {
const {basket} = this.state;
const { value_key } = this.state;
this.state.value_key = "new vk";
console.log("value_key in Form: " + value_key);
const { sharedVar } = this.state;
this.state.sharedVar = "Hello greeting";
console.log("sharedVar in Form: " + sharedVar);
return (
<div>
<InputList data={this.state.data} onChange={this.handleChange.bind(this)} />
</div>
);
}
}
export default Form```
<br>

Haa, the newbie life...!
So after more study and getting to learn the entire framework better the solution is thus.
(i) How can I share the push the basket object to a waiting parent state object?
Create classes - (some people argue for functional components) that are distinct and do the one thing well. The Form component renders the modal, collects the form values and submits those values to the parent through props.
In Parent, make ready to receive child props
<Cakes onSubmitChild={this.getFormValues} />
Then send the values to the parent component from Child like this:
this.props.onSubmitChild(this.state.productField)
(ii) How can I submit the form after console logging the form objects?
The Parent has the submit to server button. After the Child has submitted the form values to the Parent, it is left to create a JSON object and submit it to the server using AJAX (or whatever the coder prefers).
Note that there will be CORS issues to iron out on the server side to resolve this, but I recommend the cors library in npm.

Related

display button upon typing input react

I want to be able to type into my input fields, and then have a button show up beside it upon typing that says submit edit. right now, I have a button that always is there, but I want it to only show up upon typing. this is all in react btw. so far, I have tried jquery, but react doesn't like it.
here's the whole page, to avoid any confusion of what I am doing and where my stuff is located.
import React, { Component } from "react";
import axios from "axios";
import "../styles/TourPage.css";
class TourPage extends Component {
constructor(props) {
super(props);
this.state = {
myData: [],
isLoading: true,
};
}
componentDidMount() {
axios
.get("/getResults")
.then((res) => {
this.setState({
myData: res.data
});
})
.catch((error) => {
// Handle the errors here
console.log(error);
})
.finally(() => {
this.setState({
isLoading: false
});
});
}
deleteById = (id) => {
console.log(id)
axios
.post(`/deleteDoc`, {id: id} )
.then(() => {
console.log(id, " worked")
window.location = "/tour"
})
.catch((error) => {
// Handle the errors here
console.log(error);
})
}
editById = (id, siteLocation, Services, cnum) => {
console.log(id, siteLocation, Services, cnum)
axios
.post(`/editDoc`, JSON.stringify({id: id, location: siteLocation, Services: Services, cnum: cnum}),{
headers: {
"Content-Type": "Application/json"
}
} )
.then(() => {
console.log(id, " worked")
window.location = "/tour"
})
.catch((error) => {
// Handle the errors here
console.log(error);
})
}
render() {
// You can handle the loader part here with isLoading flag. In this case No data found will be shown initially and then the actual data
let { myData, isLoading } = this.state;
return (
<table id="customers">
<tr>
<th>siteLocation</th>
<th>Services</th>
<th>cnum</th>
</tr>
{myData.length > 0
? myData.map(({ location, Services, cnum, _id }, index) => (
<tr key={index}>
<td><input type="text" placeholder={location} name="location" id="location" /> </td>
<td><input type="text" placeholder={Services} name="Services" id="Services" /> </td>
<td><input type="text" placeholder={cnum} name="cnumhide" id="cnumhide" /> </td>
<td><input type="hidden" placeholder={cnum} name="cnum" id="cnum" /> </td>
<button
onClick={(e) => {
e.preventDefault();
this.deleteById(_id);
}}
disabled={isLoading}
>
Delete
</button>
<button
onClick={(e) => {
e.preventDefault();
var siteLocation = document.getElementById('location').value
var Services = document.getElementById('Services').value
var cnum = document.getElementById('cnum').value
this.editById(_id, siteLocation, Services, cnum)
}}
>
Submit Edit
</button>
</tr>
))
: "No Data Found"}
</table>
);
}
}
const script = document. createElement("script"); $('input').keyup(function(){
if($.trim(this.value).length > 0)
$('#location').show()
else
$('#location').hide()
});
export default TourPage;
thanks 4 the help in advance.
You can use onfocus() in the text element. If you want to hide the button, use onfocusout() or in case if you want to track only after input has changed, use onchange() event
...
//class function
onTyping =()=>{
this.setState({
showSubmit:true
})
}
...
//render part
render(){
...
//input which you want to track typing
<input type="text" onfocus={()=>this.onTyping()} placeholder={location} name="location" id="location" />
...
//element submit button
{this.state.showSubmit && <button
onClick={(e) => {
e.preventDefault();
var siteLocation = document.getElementById('location').value
var Services = document.getElementById('Services').value
var cnum = document.getElementById('cnum').value
this.editById(_id, siteLocation, Services, cnum)
}}
>
Submit Edit
</button>}
...
Here is an example that helps you,
const {
useState
} = React;
const Test = () => {
const [show, setShow] = useState(false);
const handleChange = (event) => {
if (event.target.value.length > 0)
setShow(true);
else
setShow(false)
}
return ( <div>
<input type = "text"
onChange = {
(event) => handleChange(event)
}/>
{show && < button > Submit changes now! </button>}
</div>
)
}
ReactDOM.render( < Test / > ,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
There is a way to avoid jquery and continue using your react class component to achieve this.
Map over state.myData to render each item with an input and a button.
Use the array index with the input's onChange event callback to add the inputValue into the correct array item's object within state.
Use the array index with the button's onClick event callback to get the item from state.myData before sending it to the server.
If there is an inputValue for the item, you can conditionally render the button.
import React, { Component } from "react";
import axios from "axios";
class TourPage extends Component {
constructor(props) {
super(props);
this.state = {
myData: [],
isLoading: true
};
}
componentDidMount() {
axios
.get("https://rickandmortyapi.com/api/character")
.then((res) => {
this.setState({
myData: res.data.results
});
})
.finally(() => {
this.setState({
isLoading: false
});
});
}
handleChangeInput = ({ target }, index) => {
const newData = [...this.state.myData];
newData[index].inputValue = target.value;
this.setState({
myData: newData
});
};
handleSubmitEdit = (index) => {
const item = this.state.myData[index];
// submit the edit to the api
console.log(
`Clicked on 'submit edit' for ${item.name} with value ${item.inputValue}`
);
};
render() {
let { myData, isLoading } = this.state;
if (isLoading) {
return "loading...";
}
return (
<div>
{myData.map(({ name, status, species, inputValue }, index) => {
return (
<div key={index}>
<p>{`${name} - ${species} - ${status}`}</p>
<input
type="text"
onChange={(e) => this.handleChangeInput(e, index)}
value={inputValue || ""}
/>
{inputValue && (
<button onClick={() => this.handleSubmitEdit(index)}>
Submit Edit
</button>
)}
</div>
);
})}
</div>
);
}
}
export default TourPage;
If you wanted to have an input per field within each row, you could make some small changes and save your edits to the item's state within a nested object.
Then you could check if there was anything inside that row's edits object to conditionally show the submit button per row.
import React, { Component } from "react";
import axios from "axios";
import isEmpty from "lodash.isempty";
import pick from "lodash.pick";
class TourPage extends Component {
constructor(props) {
super(props);
this.state = {
myData: [],
isLoading: true
};
}
componentDidMount() {
axios
.get("https://rickandmortyapi.com/api/character")
.then((res) => {
this.setState({
// here we create an empty 'edits' object for each row
myData: res.data.results.map((d) => ({
...pick(d, ["name", "status", "species"]),
edits: {}
}))
});
})
.finally(() => {
this.setState({
isLoading: false
});
});
}
handleChangeInput = ({ target }, index) => {
const newData = [...this.state.myData];
const { value, name } = target;
newData[index].edits[name] = value;
this.setState({
myData: newData
});
};
handleSubmitEdit = (index) => {
const item = this.state.myData[index];
// submit the edit to the api
console.log(`Clicked on 'submit edit' for ${item.name} with edits:`);
console.log(item.edits);
console.log("Updated item: ");
const { edits, ...orig } = item;
const newItem = { ...orig, ...edits };
console.log(newItem);
// Once saved to api, we can update myData with newItem
// and reset edits
const newData = [...this.state.myData];
newData[index] = { ...newItem, edits: {} };
this.setState({
myData: newData
});
};
showButton = (index) => {
const { edits } = this.state.myData[index];
return !isEmpty(edits);
};
render() {
let { myData, isLoading } = this.state;
if (isLoading) {
return "loading...";
}
return (
<table>
<tbody>
{myData.map((row, index) => {
const { edits, ...restRow } = row;
return (
<tr key={index}>
{Object.keys(restRow).map((col) => {
return (
<td>
<label>
{col}:
<input
name={col}
value={edits[col] || restRow[col]}
onChange={(e) => this.handleChangeInput(e, index)}
/>
</label>
</td>
);
})}
<td>
{this.showButton(index) && (
<button onClick={() => this.handleSubmitEdit(index)}>
Submit Edit
</button>
)}
</td>
</tr>
);
})}
</tbody>
</table>
);
}
}
export default TourPage;

How to pass quantity from component to component in ReactJS where they are independent but live in the same parent

I have the following class Component that is handling some quantity changes an updating the price of a sku from stripes API.
The crux of the issue is that I need the quantity for the sku inside the Cart component, but the quantity state is handled inside the Product component, and it needs to be separate because you can change the sku in the dropdown menu and the product will change state, so if the cart component is in the product then the state clears and is not persistent. Also I will need a way for the cart to add to the state instead of just replacing it on setSkuObject not sure if that's possible either
Any help would be greatly appreciated
Product component
class Product extends React.Component {
constructor(props) {
super(props)
this.state = {
stripe: null,
quantity: 1,
price: props.price,
skuObject: null,
}
this.handleInputChange = this.handleInputChange.bind(this)
}
handleInputChange(event) {
const target = event.target
const { quantity } = this.state
const { price } = this.props
this.setState({
quantity: parseInt(target.value),
})
this.setState({
price: price * parseInt(target.value),
})
}
render() {
const { id, currency, name, productId } = this.props
const { quantity, price, skuObject } = this.state
//console.log(quantity);
const priceFloat = (price / 100).toFixed(2)
const formattedPrice = Intl.NumberFormat("en-US", {
style: "currency",
currency,
}).format(priceFloat)
return (
<>
<form className="name-buy">
<label>
Quantity
<input
type="number"
name="amount"
min="1"
value={this.state.quantity}
onChange={this.handleInputChange}
/>
</label>
</form>
<form
className="submit"
onSubmit={this.handleSubmit(id, productId, quantity)}
>
<div>
<h2>
{name} ({formattedPrice})
</h2>
<button type="submit">Buy Now</button>
</div>
</form>
</>
)
}
}
cart class component
class Cart extends React.Component {
constructor(props) {
super(props)
this.state = {
sku: null,
quantity: null,
}
}
render() {
const { skuObject } = this.props
return <CartBox>Cart {skuObject} {/*would like to have quantity here*/} </CartBox>
}
}
It lives inside A StoreDetails functional component
const StoreDetails = ({ data, location }) => {
const setSkuOnce = data.allDatoCmsStore.edges.map(
({ node: product }, index) => {
if (product.defaultProduct === true) {
//console.log(product.skuId);
return product.skuId
}
}
)
//console.log(setSkuOnce)
var filtered = setSkuOnce.filter(function(el) {
return el != null
})
const [value, setValue] = useState({
skuId: filtered[0],
})
const run = event => {
//console.log(event);
setValue({ skuId: event })
}
const [skuObject, setSkuObject] = useState()
const handleCart = (sku, productId, quantity) => {
return event => {
event.preventDefault()
setSkuObject(sku)
}
}
return (
<Layout>
<StoreHero>
<Link to="/store">
<BsArrowLeft />
Back
</Link>
<Alert test="test" hello="hello" hash={location.hash} />
{/* <ShowItem props={data}/> */}
<Cart skuObject={skuObject} />
{data.allDatoCmsStore.edges.map(({ node: sku }) => (
<React.Fragment key={sku.skuId}>
{value.skuId === sku.skuId ? (
<ProductGrid>
<Img fluid={sku.image.fluid} />
<ProductCol>
<h1>{sku.productTitle}</h1>
<FirstThreeWrap>
<form className="variant">
<label>
<span>Product Variant</span>
<select
value={value.skuId}
onChange={event => run(event.target.value)}
>
{data.allDatoCmsStore.edges.map(({ node: sku }) => (
<React.Fragment key={sku.skuId}>
<option value={sku.skuId}>{sku.title}</option>
</React.Fragment>
))}
</select>
</label>
</form>
{/* <ShowItem setValue={setValue}/> */}
<Product
key={sku.id}
id={sku.skuId}
productId={sku.productId}
currency="cad"
price={sku.price}
name={sku.title}
/>
<button onClick={handleCart(sku.skuId, sku.productId)}> {/* I NEED TO PASS THE QUANTITY IN HERE */}
Add to Cart
</button>
</FirstThreeWrap>
<div
className="description"
dangerouslySetInnerHTML={{ __html: sku.productDescription }}
/>
<QuestionForm skuId={sku.skuId} name={sku.title} />
</ProductCol>
</ProductGrid>
) : null}
</React.Fragment>
))}
</StoreHero>
</Layout>
)
}
If I've parsed all this correctly, then it would seem you could achieve it by simply moving the "Add To Cart" button inside Product. I can't see any reason not to as they are rendered one after the other and use exactly the same data from the map function. You would just pass the handler down like so:
<Product
key={sku.id}
id={sku.skuId}
productId={sku.productId}
currency="cad"
price={sku.price}
name={sku.title}
handleCart={handleCart}
/>
And then in Product render it like this:
return (
<>
<form>
...
</form>
<button onClick={() => this.props.handleCart(id, productId, this.state.quantity)}>
Add to Cart
</button>
</>
)

React: How to send data on Popup close?

I have this Popup that I built in my React app. It's, in a sense, another page inside the Popup. In this Popup, there is a list. There are no forms at all in this Popup window. I only have another popup inside this popup with a submission form that adds another item to the list.
Therefore, what I'm attempting to do is to submit the list to the Parent component when I click on the "Close" button to close the Popup window. How do I do that?
Here's my code:
SingleBox.js
import React, { Component } from "react";
import SecurityForm from "../SecurityForm/index";
import PriceForm from "../PriceForm/index";
export default class SingleSecuritybox extends Component {
constructor(props) {
super(props);
this.state = {
showPopup: false,
showPricePopup: false, //don't show popup
pricelist: this.props.price
};
}
/* toggle and close popup edit form window */
togglePopup = () => {
this.setState(prevState => ({
showPopup: !prevState.showPopup
}));
};
togglePricePopup = () => {
this.setState(prevState => ({
showPricePopup: !prevState.showPricePopup
}));
};
/* handles edit current security form submissions */
handleEditSecuritySubmission = editSecurity => {
const { editCurrentSecurity, id } = this.props;
this.togglePopup();
editCurrentSecurity({ ...editSecurity, id });
};
updatePrice = updatePrice => {
const { updatePriceList, id } = this.props;
this.togglePricePopup();
updatePriceList({...updatePrice, id});
console.log("editing price", updatePrice);
};
/* handles delete current security form submissions */
handleDeleteSecurity = () => {
const { deleteSecurity, id } = this.props;
// toggle the pop up (close)
this.togglePopup();
// sends the id back to App's "this.deleteSecurity"
deleteSecurity(id);
};
render() {
return (
<div className="box">
<article className="securitytable">
<div className="title">
<h2>
<strong>{this.props.name}</strong>
</h2>
<hr className="lightgray-line" />
</div>
<table>
<tbody>
<tr>
<td className="isin-width">{this.props.isin}</td>
<td className="country-width">{this.props.country}</td>
<td>
<button type="button" className="price-btn" onClick={this.togglePricePopup}>Prices</button>
{this.state.showPricePopup ? (
<PriceForm
pricelist= {this.props.price}
updatePrice={ this.updatePrice }
addPrice={this.props.addPrice}
closePopup= {this.togglePricePopup}
/>
) : null}
</td>
<td className="editing-btn">
<button
type="button"
className="edit-btn"
onClick={this.togglePopup}
>
Edit
</button>
{this.state.showPopup ? (
<SecurityForm
{...this.props}
handleEditSecuritySubmission={ this.handleEditSecuritySubmission }
handleDeleteSecurity={this.handleDeleteSecurity}
cancelPopup={this.togglePopup}
/>
) : null}
</td>
</tr>
</tbody>
</table>
</article>
</div>
);
}
}
This code in question is this list that'll open in Popup window which is a child componenet:
<button type="button" className="price-btn" onClick={this.togglePricePopup}>Prices</button>
{this.state.showPricePopup ? (
<PriceForm
pricelist= {this.props.price}
updatePrice={ this.updatePrice }
addPrice={this.props.addPrice}
closePopup= {this.togglePricePopup}
/>
) : null}
In this child component, which is Price Popup:
import React, { Component } from "react";
import PriceBox from "../SinglePricebox/index";
import AddPriceForm from "../AddPriceForm/index";
export default class PriceForm extends Component {
constructor(props) {
super(props);
this.state = {
priceArr: this.props.pricelist,
showPricePopup: false,
addPricePopup: false,
isToggleOn: true,
date: props.date || "",
number: props.number || ""
};
}
updateInput = ({ target: { name, value } }) =>
this.setState({ [name]: value });
togglePopup = () => {
this.setState(prevState => ({
showPopup: !prevState.showPopup
}));
};
togglePricePopup = () => {
this.setState(prevState => ({
showPricePopup: !prevState.showPricePopup
}));
};
addPricePopup = () => {
this.setState(prevState => ({
addPricePopup: !prevState.addPricePopup
}));
};
/* adds a new price to the list */
addPrice = newPrice => {
this.setState(prevState => ({
addPricePopup: !prevState.addPricePopup,
// spreads out the previous list and adds the new price with a unique id
priceArr: [...prevState.priceArr, { ...newPrice }]
}));
};
handleListSubmit = () => {
const { priceArr } = this.state;
const { updatePrice } = this.props;
const fields = {priceArr};
this.setState(() => {
// if (addPrice) addPrice(fields);
updatePrice(fields);
});
console.log("submission", fields);
};
render() {
return (
<div className="popup">
<div className="popup-inner">
<div className="price-form">
<h2>Prices</h2>
<div className="scroll-box">
{this.state.priceArr.map((props) => (
<PriceBox
{...props}
key={props.date}
/>
))}
</div>
<div className="buttons-box flex-content-between">
<button
type="button"
onClick={this.addPricePopup}
className="btn add-button">Add +</button>
{this.state.addPricePopup && (
<AddPriceForm
addPrice={this.addPrice}
cancelPopup={this.addPricePopup}
/>
)}
<div className="add-btns">
<button
type="button"
onClick={this.handleListSubmit}
className="btn cancel-button"
>
Close
</button>
</div>
</div>
</div>
</div>
</div>
);
}
}
What I'm attempting to do is to send the data (the list array) back to the Parent component on close, but I notice that I can send the data back but I couldn't close the window...
<button
type="button"
onClick={this.handleListSubmit}
className="btn cancel-button"
>
Close
</button>
How do I do this? I cannot add something like this.props.closePopup(); in the handleListSubmit function because while it can close the window, it prevents the list array from being submitted and passed to the Parent component.
You can use parent callback function to send data from child to parent.
on child
handleListSubmit = () => {
...
this.props.onSummited(data)
}

how to Uncheck Checkbox.Group in Antd React.js

I have the following Code using https://ant.design/components/checkbox/, and Trying to uncheck when a checkbox has been checked.
I don't want to check all if button is click, just Uncheck all or the one checkbox selected.
constructor(props) {
super(props);
this.state = {
checked: true,
}
this.onChange = this.onChange.bind(this);
}
onChange(value) {
this.props.onChangeSession(value)
}
onChangeBox = e => {
this.setState({
checked: e.target.checked,
});
};
unChecked = () => {
this.props.onChangeSession([])
this.setState({
checked: false
});
};
render () {
const {
data,
} = this.props
return (
<div>
<Button type="primary" size="small" onClick={this.unChecked}>
Uncheck
</Button>
<Checkbox.Group
style={{ width: '100%' }}
onChange={this.onChange}>
<div>
<div className="filter-subhead">Track</div>
{data.map(i =>
<div className="filter-item">
<Checkbox
checked={this.state.checked}
onChange={this.onChangeBox}
value={i}>{i}</Checkbox>
</div>
)}
</div>
</Checkbox.Group>
</div>
)
}
Any help will be appreciate!
Working Link
The toggle on the checkbox wasn't working due to Checkbox.Group, you can simply use Checkbox
Regarding Checkbox State:
You can't have a single state for all the checkboxes, so you will need to have an array of bool which will serve as the state for each checkbox item.
In the example, I have initialized checkbox state on componentDidMount and it creates an array ([false,false,false,...]) and the exact same thing is used for resetting on Uncheck. (Possibility of refactoring in my code)
User assigned state will decide whether to check the checkbox or not.
import React from "react";
import ReactDOM from "react-dom";
import { Button, Checkbox } from "antd";
import "antd/dist/antd.css";
import "./index.css";
let data = [3423, 3231, 334234, 55345, 65446, 45237];
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
checkboxArray: []
};
// this.onChange = this.onChange.bind(this);
}
componentDidMount() {
let createEmptyArray = new Array(data.length).fill(false);
this.setState({ checkboxArray: createEmptyArray });
}
onChange(e) {
console.log(e.target);
}
onChangeBox = (e, i) => {
let checkBoxCurrentState = this.state.checkboxArray;
checkBoxCurrentState[i] = !checkBoxCurrentState[i];
this.setState({
checkboxArray: checkBoxCurrentState
});
};
unChecked = e => {
let resetArray = new Array(data.length).fill(false);
this.setState({
checkboxArray: resetArray
});
};
render() {
const { data } = this.props;
return (
<div>
<Button type="primary" size="small" onClick={this.unChecked}>
Uncheck
</Button>
<div>
<div className="filter-subhead">Track</div>
{data.map((i, index) => (
<div className="filter-item">
<Checkbox
checked={this.state.checkboxArray[index] ? true : false}
onChange={e => this.onChangeBox(e, index)}
value={index}
>
{JSON.stringify(this.state.checkboxArray[index])}
</Checkbox>
</div>
))}
</div>
{JSON.stringify(this.state.checkboxArray)}
</div>
);
}
}
ReactDOM.render(<App data={data} />, document.getElementById("root"));
Simple copy and paste the above code and add props where required.
And if you want to user Checkbox.Group, will need to update the onChange method of CheckBox.Group
let data = ['Apple', 'Pancakes', 'Butter', 'Tea', 'Coffee'];
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
checkboxArray: []
};
// this.onChange = this.onChange.bind(this);
}
componentDidMount() {
let createEmptyArray = new Array(this.props.data.length).fill(false);
this.setState({ checkboxArray: createEmptyArray });
}
onChangeBox = (e, i) => {
let checkBoxCurrentState = this.state.checkboxArray;
checkBoxCurrentState[i] = !checkBoxCurrentState[i];
this.setState({
checkboxArray: checkBoxCurrentState
});
};
unChecked = () => {
let resetArray = new Array(data.length).fill(false);
this.setState({
checkboxArray: resetArray
});
};
render() {
const { data } = this.props;
return (
<div>
<button onClick={this.unChecked}>Clear All</button>
{this.props.data.map((single, index) => (
<div>
<input
type="checkbox"
id={index}
name="scales"
checked={this.state.checkboxArray[index]}
onChange={e => this.onChangeBox(e, index)}
/>
<label for={index}>{single}</label>
</div>
))}
</div>
);
}
}
ReactDOM.render(<App data={data} />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
If you're going to use a map to dynamically generate the checkboxes, using state to keep track of checked value will be tricky. You should not do this.
What you should do is not include the checked prop at all in the component.
<Checkbox
onChange={this.onChangeBox}
value={i}>{i}
</Checkbox>
The checkbox should still check even if you do not include the checked prop. It's a little strange, I know.
Instead just pass the value into the onChangeBox function and handle all your logic there and set a state value on change.
I just tested it out and it works.

How to Update This Reactjs Select

I have this wrapper class that is used because I am using Formik and the FieldArray
import React, { Component } from "react";
import { ReactDOM } from "react-dom";
import Select from "react-select";
import { observer } from "mobx-react";
import { axiosInstance } from "../stores/AxiosInstance";
#observer
export default class CountryStateSelectComponent extends React.Component {
constructor(props) {
super(props);
this.state = { stateOptions: [] };
}
handleCountryChange = value => {
const that = this;
axiosInstance
.get(`/States?countryId=${value.value}`)
.then(function(response) {
that.props.onChange(that.props.countryName, value);
that.props.onChange(that.props.stateName, null);
const states = response.data.map(state => {
return { label: state.name, value: state.id };
});
// if I move out state select code then won't need to update state here but don't know how to call something like updateState(record)
that.setState({
stateOptions: states
});
});
};
handleStateChange = value => {
console.log(this.props.stateName, value)
this.props.onChange(this.props.stateName, value);
};
handleCountryBlur = () => {
this.props.onBlur(this.props.countryName, true);
};
handleStateBlur = () => {
this.props.onChange(this.props.stateName, true);
};
render() {
const props = this.props;
return (
<React.Fragment>
<div className="field">
<label className="label">Country</label>
<div className="control">
<Select
options={props.options}
isMulti={props.isMulti}
onChange={this.handleCountryChange}
onBlur={this.handleCountryBlur}
closeMenuOnSelect={props.closeMenuOnSelect}
/>
{this.props.CountryError}
</div>
</div>
<div className="field">
<label className="label">State/Province</label>
<div className="control">
<Select
options={this.state.stateOptions}
onChange={this.handleStateChange}
onBlur={this.handleStateBlur}
/>
{this.props.StateError}
</div>
</div>
</React.Fragment>
);
}
}
However what I found is that when the State gets selected the value does not get stored in Formik(it gets stored as undefined and sometimes true).
So now I am thinking maybe moving out the State Zip out and making it's own wrapper or something but I don't know how to get the "states" that came back and populate the correct state box as they can generate many.
#inject("AccountSetupStore")
#observer
export default class MyComponent extends Component {
constructor(props) {
super(props);
this.state = { records: [this.generateRecord(1, true, true)] };
}
componentDidMount() {
const accountSetupStore = this.props.AccountSetupStore;
accountSetupStore.getCountries();
}
updateState(record) {
// would like to call this method that can somehow update the record
// propblem is I don't seem to have access to props when this function is being called from the CountryStateSelectComponent
}
render() {
const props = this.props;
const accountSetupStore = props.AccountSetupStore;
const countries = [];
for (const country of accountSetupStore.countries) {
countries.push({ value: country.id, label: country.name });
}
return (
<section className="accordions">
<Formik
initialValues={{
records: this.state.records
}}
onSubmit={(
values,
{ setSubmitting, setErrors}
) => {
console.log(values,"values");
}}
validationSchema={Yup.object().shape({
branches: Yup.array()
.of(
Yup.object().shape({
})
)
})}
render={({
values,
setFieldValue,
setFieldTouched,
}) => (
<FieldArray
name="records"
render={arrayHelpers => (
<Form>
{values.records.map((record, index) => {
return (<article}>
<CountryStateSelectComponent options={countries}
onChange={setFieldValue}
countryName={`records[${index}].selectedCountry`}
stateName={`records[0].selectedState`}
onBlur={setFieldTouched}
isMulti={false}
index = {index}
closeMenuOnSelect={true}
CountryError = {<ErrorMessage name={`records[${index}].selectedCountry`}/>}
StateError= {<ErrorMessage name={`records[${index}].selectedState`}/>}
/>
</article>)
})}
</Form>
)}
/>
)}
/>
</section>
);
}
}
React Select onChange sends the value to the method supplied
const onStateChange = (selectedOption, {action}) => {
//do something with the selectedOption according to the action
}
<Select onChange={onStateChange} />
See the documentation for the onChange in the Props documentation.

Categories

Resources