A single click on a button updates all the buttons but I want to change the state of that particular clicked button. Please check the image links below and the code.
import React from 'react';
import './MenuCard.css';
class MenuCard extends React.Component {
constructor(props) {
super(props);
this.state = {
showButton: false,
hideButton: true,
aValue: 1,
breads: [],
category: [],
ids: 1,
btnVal: 'Add'
};
}
onKeyCheck = (e) => {
this.state.breads.map(filt => {
if (filt.id === e.target.id) {
console.log(e.target.id + ' and ' + filt.id)
return (this.setState({showButton: !this.state.showButton, hideButton: !this.state.hideButton}));
}
})
}
onShowButton = () => {
this.setState({showButton: !this.state.showButton, hideButton: !this.state.hideButton})
}
onValueIncrease = () => {
this.setState({aValue: this.state.aValue + 1});
}
onValueDecrease = () => {
this.setState({aValue: this.state.aValue - 1});
}
componentDidMount() {
fetch('http://localhost:3000/menu/food_category', {
method: 'get',
headers: {'content-type': 'application/json'}
})
.then(response => response.json())
.then(menudata => {
this.setState({category: menudata.menu_type})
console.log(this.state.category)
})
fetch('http://localhost:3000/menu', {
method: 'get',
headers: {'content-type': 'application/json'}
})
.then(response => response.json())
.then(menudata => {
this.setState({breads: menudata })
})
}
render() {
return (
<div>
{this.state.category.map(types => {
return (<div>
<div className="menu-head">{types}</div>
< div className="container-menu">
{this.state.breads.map((d, id)=> {
if (d.category === types) {
return (
<div>
<div className="content" key={id} id={d.id}>
<div className="items"> {d.item_name}</div>
<div className="prices"> {d.price} Rs.</div>
{this.state.showButton ?
<div>
<button
className="grp-btn-minus"
onClick={this.state.aValue <= 1 ?
() => this.onShowButton() :
() => this.onValueDecrease()}>-
</button>
<input className="grp-btn-text" type="text"
value={this.state.aValue} readOnly/>
<button id={d.id}
className="grp-btn-plus"
onClick={() => this.onValueIncrease()}>+
</button>
</div> :
<button id={d.id} key={id}
onClick={ this.onKeyCheck}
className="add-menu-btn">
add
</button>
}
</div>
</div>
)
}
})}
</div>
</div>)
})}
</div>
)
}
}
export default MenuCard;
This is the first image of multiple rendering of component Add buttons
Here is the problem that all buttons get updated on single click
You're using an array of items but refering to a single, shared value in handlers. De facto you're using a few shared values: showButton, hideButton, aValue), 2/3 unnecessary ;)
First - aValue for each item should be stored in a structure - array or object. It could be an order = {} - object with id-keyed properties with amounts as values like this:
order = {
'masala_id': 1,
'kebab_id' : 2
}
Event handler (for 'add') should check if id for choosen product already exist in order object (as property name) and update amount (+/-) or create new one with 1 value (and remove property when decreased amount = 0).
In practice order should also contain a price - it seams like duplicating data but it will be much easier to count total order value.
order = {
'masala_id': {
'amount': 1,
'price': 20,
},
'kebab_id' : {
'amount': 2,
'price': 180,
}
}
Item doesn't need to be a component but it's much easier to maintain it, keep it readable etc.
This way we can simply pass already ordered amount and conditionally render buttons:
<Product id={d.id}
name={d.item_name}
price={d.price}
amount={order[d.id] ? order[d.id].amount : 0 }
amountHandler={this.changeAmountHandler}
/>
Product should be slightly improved and simplified (f.e. key is needed on top div):
class Product extends React.Component {
render () {
const (id, name, price, amount, amountHandler} = this.props;
const showIncrease = !!amount; // boolean, it also means "don't show add button"
return (
<div key={id} >
<div className="content">
<div className="items">{name}</div>
<div className="prices">{price} Rs.</div>
{showIncrease ?
<div>
<button
className="grp-btn-minus"
onClick={(e) => { amountHandler(e, id, -1) }}
>-</button>
<input className="grp-btn-text" type="text"
value={amount}
readOnly/>
<button
className="grp-btn-plus"
onClick={(e) => { amountHandler(e, id, 1) }}
>+</button>
</div> :
<button
onClick={(e) => { amountHandler(e, id, 1) }}
className="add-menu-btn"
>add</button>
}
</div>
</div>
)}}
This way you can handle all events in one handler, keep entire order state in main component... in case of performance problems just use PureComponent.
It looks like all the buttons are sharing the same state. You could try breaking the button up into its own component, and then move the state that button needs into there. That way when you click a button the state of that one particular button is updated, and not the state of the parent component that contains all the buttons.
Related
I am pretty new to react. So I have one parent component which has two child components. These 2 children are the lists that should be displayed. So far I figured out how to transfer the data between two lists by checking the status property of the data. I am not able to understand how to add data into the separate lists and edit them since the parent component renders the 2 lists. Can anyone explain how to add and edit new data that the user will enter? Should I create new states and props on the Items page or should I create them on the child component page? I am pretty confused.
import React,{useState,useEffect} from 'react'
import { Completed } from './Completed'
import { Pending } from './Pending'
export const Items = () => {
const [items,setItems]=useState([
{
id: 1,
title:'Workout',
status:'Pending'
},
{
id: 2,
title:'Read Books',
status:'Pending'
},
{
id: 3,
title:'Cook Pizza',
status:'Pending'
},
{
id: 4,
title:'Pay Bills',
status:'Completed'
},
{
id: 5,
title:' Watch Big Short',
status:'Completed'
},
{
id: 6,
title:' Make nutrition Plan',
status:'Pending'
}
])
const updateStatus=(id,newStatus)=>{
let allItems=items;
allItems=allItems.map(item=>{
if(item.id===id){
console.log('in here')
item.status=newStatus;
}
return item
})
setItems(allItems)
}
return (
<div class="items">
<Pending items={items} setItems={setItems} updateStatus={updateStatus}/>
<Completed items={items} setItems={setItems} updateStatus={updateStatus}/>
</div>
)
}
import React from 'react'
export const Pending = ({items,setItems,updateStatus}) => {
return (
<div className="pending">
<h1>LEFT</h1>
{
items && items.map(item=>{
if(item && item.status==='Pending')
return <><p className="item" key={item.id}>{item.title} <button className="mark_complete" key={item.id} onClick={()=>{updateStatus(item.id,'Completed')}}>Move Right</button></p></>
})
}
</div>
)
}
import React from 'react'
export const Completed = ({items,setItems,updateStatus}) => {
return (
<div className="completed">
<h1>RIGHT</h1>
<form onSubmit={this.addItem}>
<input placeholder="enter task">
</input>
<button type="submit">add</button>
</form>
{
items && items.map(item=>{
if(item && item.status==='Completed')
return <><p className="item" key={item.id}>{item.title} <button className="mark_pending" key={item.id} onClick={()=>{updateStatus(item.id,'Pending')}}> Move Left</button></p> </>
})
}
</div>
)
}
I have attached the 3 components which are Items, Pending and Completed above.
It's almost always better to have the state in the parent and pass down props to the children. So you want to keep your items state where it is. You can create an addItem function and pass it down as a prop to any child.
I don't think it makes sense to be able to add items from both lists since new items should be 'Pending'. So I would recommend that you put your add form in a new component AddItem which would be a third child of Items. Once AddItem calls the addItem function from props, that item will get saved to the state in items and it will show up in the Pending list automatically.
If all new items have status 'Pending' then the only information that we should need to add an item is the title of the task.
This function goes in Items:
const addItem = (title) => {
// set state using a callback function of current state
setItems((current) => {
// the highest number of all current ids, or 0 if empty
const maxId = current.reduce((max, o) => Math.max(max, o.id), 0);
// the next id is the max plus 1
const id = maxId + 1;
// add new item to the current - concat won't mutate the array
return current.concat({
id,
title,
status: "Pending"
});
});
};
Your AddItem component uses a controlled input to create the text for the new item.
export const AddItem = ({ addItem }) => {
const [title, setTitle] = useState("");
const handleSubmit = (e) => {
// prevent form submission from reloading the page
e.preventDefault();
// call the addItem function with the current title
addItem(title);
// clear the form
setTitle("");
};
return (
<form onSubmit={handleSubmit}>
<input
placeholder="enter task"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<button type="submit">add</button>
</form>
);
};
Inside the return of Items, include your form:
<AddItem addItem={addItem} />
Unrelated to the question at hand, there are a few other improvements that you can make to your code.
Your updateStatus function actually mutates the current item. You should instead create a new object for the changed item by copying everything except the status.
You are getting warnings about unique keys because the key must be on the outermost component inside the .map(). You put a fragment <> outside the <p> which has the key, so remove the fragment.
In my opinion the filtering of which item goes in each list should be done by the parent. Your Completed and Pending components are extremely similar. You should combine them into one component. Everything that is different between the two, such as texts and class names, can be controlled by the props that you pass in.
import React, { useState } from "react";
export const ItemsList = ({
items,
title,
className,
buttonText,
onClickButton
}) => {
return (
<div className={className}>
<h1>{title}</h1>
{items.map((item) => (
<p className="item" key={item.id}>
<span className="item_title">{item.title}</span>
<button
className="move_item"
key={item.id}
onClick={() => {
onClickButton(item.id);
}}
>
{buttonText}
</button>
</p>
))}
</div>
);
};
// example of how to compose components
// this keeps the same setup that you had before, but without repeated code
export const Completed = ({ items, updateStatus }) => {
return (
<ItemsList
title="RIGHT"
buttonText="Move Left"
className="completed"
items={items.filter((item) => item.status === "Completed")}
onClickButton={(id) => updateStatus(id, "Pending")}
/>
);
};
export const AddItem = ({ addItem }) => {
const [title, setTitle] = useState("");
const handleSubmit = (e) => {
// prevent form submission from reloading the page
e.preventDefault();
// call the addItem function with the current title
addItem(title);
// clear the form
setTitle("");
};
return (
<form onSubmit={handleSubmit}>
<input
placeholder="enter task"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<button type="submit">add</button>
</form>
);
};
export const Items = () => {
const [items, setItems] = useState([
{
id: 1,
title: "Workout",
status: "Pending"
},
{
id: 2,
title: "Read Books",
status: "Pending"
},
{
id: 3,
title: "Cook Pizza",
status: "Pending"
},
{
id: 4,
title: "Pay Bills",
status: "Completed"
},
{
id: 5,
title: " Watch Big Short",
status: "Completed"
},
{
id: 6,
title: " Make nutrition Plan",
status: "Pending"
}
]);
const addItem = (title) => {
// set state using a callback function of current state
setItems((current) => {
// the highest number of all current ids, or 0 if empty
const maxId = current.reduce((max, o) => Math.max(max, o.id), 0);
// the next id is the max plus 1
const id = maxId + 1;
// add new item to the current - concat won't mutate the array
return current.concat({
id,
title,
status: "Pending"
});
});
};
const updateStatus = (id, newStatus) => {
setItems((current) =>
// arrow function without braces is an implicit return
current.map((item) =>
item.id === id
? // copy to new item if id matches
{
...item,
status: newStatus
}
: // otherwise return the existing item
item
)
);
};
return (
<div className="items">
<AddItem addItem={addItem} />
{/* can set the props on ItemsList here */}
<ItemsList
title="LEFT"
buttonText="Move Right"
className="pending"
items={items.filter((item) => item.status === "Pending")}
// create a function that just takes the `id` and sets the status to "Completed"
onClickButton={(id) => updateStatus(id, "Completed")}
/>
{/* or do it in a separate component */}
<Completed items={items} updateStatus={updateStatus} />
</div>
);
};
export default Items;
Code Sandbox Link
I am developing an application in React. I have a parent element wherein I have a child component(Photo) which needs to rendered multiple times(7 in below case). So I have used .map and allPhotos variable is rendered in return function.
Parent Component :
handlePhotos = (event, isSingleMulti, photoIndex) => {
console.log("Upload Photo", event.target.files, photoIndex, isSingleMulti);
}
openFileDialog(isSingleMulti, photoIndex) {
isSingleMulti === 'M' ? document.getElementById('multi-photo-input').click() : document.getElementById('single-photo-input').click();
}
let photosTemp = [1,2,3,4,5,6,7];
let allPhotos = photosTemp.map(ele => {
return <Photo key={ele} photoBoxID={ele} openFileDialog={this.openFileDialog} handlePhotos={this.handlePhotos} onDeletePhoto={this.onDeletePhoto}/>
});
In my Child component (Photo) I have a div, onClick of div I call openFileDialog which internally calls click of hidden input element(#single-photo-input). onChange of input element I call handlePhotos. Both of these functions handlePhotos and openFileDialog are defined in my parent and passed to Child (Photo) as a prop.
Now what I need is that when onChange method handlePhotos is called, I want to return each Photo photoBoxID value. Basically, I want to check which Photo component was clicked. But every time I get value as 1 instead of respective 1,2,3 etc. What wrong am I doing?
Child Component :
const UploadImage = (props) => {
console.log(props.photoBoxID);
return (
<div className="photo-root">
<div className="photo-inner-container" onClick={() => props.openFileDialog('S')}>
<span className="inner-text">+</span>
<form encType="multipart/form-data" id="single-photo-form">
<input type="file" name="file" id="single-photo-input" className="hide" accept="image/jpg, image/jpeg, image/png"
onChange={(event) => props.handlePhotos(event, 'S', props.photoBoxID)}/>
</form>
</div>
</div>
)
}
class Photo extends React.Component {
render() {
return (
true === true ? <UploadImage {...this.props}/> : <ImagePreview {...this.props}/>
)
}
}
In open dialog, you are getting element by id but you don't have unique ids so you can append index to your input ids and pass index to openDialog function and click that particular input only.
Parent component
handlePhotos = (event, isSingleMulti, photoIndex) => {
console.log("Upload Photo", event.target.files, photoIndex, isSingleMulti);
}
openFileDialog(isSingleMulti, photoIndex) {
isSingleMulti === 'M' ? document.getElementById(`multi-photo-input-${photoIndex}`).click() : document.getElementById(`single-photo-input-${photoIndex}`).click(); //appended index here
}
let photosTemp = [1,2,3,4,5,6,7];
let allPhotos = photosTemp.map(ele => {
return <Photo key={ele} photoBoxID={ele} openFileDialog={this.openFileDialog} handlePhotos={this.handlePhotos} onDeletePhoto={this.onDeletePhoto}/>
});
in your photo.js
const UploadImage = (props) => {
console.log(props.photoBoxID);
return (
<div className="photo-root">
<div className="photo-inner-container" onClick={() => props.openFileDialog('S', props.photoBoxID)}>
<span className="inner-text">+</span>
<form encType="multipart/form-data" id={`single-photo-form-${props.photoBoxID}`}>
<input type="file" name="file" id={`single-photo-input-${props.photoBoxID}`} className="hide" accept="image/jpg, image/jpeg, image/png"
onChange={(event) => props.handlePhotos(event, 'S', props.photoBoxID)}/>
</form>
</div>
</div>
)
}
class Photo extends React.Component {
render() {
return (
true === true ? <UploadImage {...this.props}/> : <ImagePreview {...this.props}/>
)
}
}
Child Component :
// as is
<div className="photo-inner-container" onClick={() => props.openFileDialog('S')}>
// to be
<div className="photo-inner-container" onClick={(event) => props.openFileDialog(event, 'S')}>
Parent Component :
// as is
openFileDialog(isSingleMulti, photoIndex) {
isSingleMulti === 'M' ? document.getElementById('multi-photo-input').click() : document.getElementById('single-photo-input').click();
}
// to be
openFileDialog = (event, isSingleMulti) => {
isSingleMulti === 'M' ? document.getElementById('multi-photo-input').click() : event.currentTarget.childNodes[1].children[0].click();
}
Hmmm...
I tried to find another way to catch the exact input a client clicks lol
I have a search input I'd like to clear after the value is submitted and the search is performed with the value. In similar questions, it was suggested to set the state of the input value to '', but I think that's what I tried and it didn't do anything.
I only have a parent and child component in my app. The parent component has a method for searching jokes (searchJokes), and it is passed down as a prop with a different name to the child component in the component instance with onFormSubmit={this.searchJokes}. In the child component, when the user enters something into the search input, its event.target.value is passed with onChange={e => props.onInputChange(e.target.value)} corresponding to the onSearchChange method in the parent prop, and the value is used to update the state of searchTerm.
I added searchTerm: '' to the end of the searchJokes method, which fetches a search according to the search term, as you can see in the parent component code below.
Parent component:
class App extends Component {
constructor() {
super();
this.state = {
searchTerm: '',
jokes: [],
isFetchingJokes: false,
isSearch: false
};
this.onSearchChange = this.onSearchChange.bind(this);
this.randomizeJokes = this.randomizeJokes.bind(this);
this.searchJokes = this.searchJokes.bind(this);
}
randomizeJokes() {
this.setState({
isFetchingJokes: true,
isSearch: false
});
fetch(
'https://icanhazdadjoke.com/',
{
method: 'GET',
headers: {
Accept: 'application/json'
}
})
.then(response => response.json())
.then(json => {
let joke = json.joke;
this.setState({
joke,
isFetchingJokes: false
});
});
}
searchJokes(limit = 15) {
// If nothing entered, user gets "Please fill out this field" message due to "required" attribute on input element
if (this.state.searchTerm !== '') {
this.setState({
isFetchingJokes: true,
isSearch: true
});
fetch(
`https://icanhazdadjoke.com/search?term=${
this.state.searchTerm
}&limit=${limit}`,
{
method: 'GET',
headers: {
Accept: 'application/json'
}
})
.then(response => response.json())
.then(json => {
let jokes = json.results;
this.setState({
jokes,
isFetchingJokes: false,
searchTerm: '' // <-- DOESN'T CLEAR INPUT
});
});
}
}
onSearchChange(value) {
this.setState({ searchTerm: value });
}
jokeRender() {
return (
<div>
{this.state.isSearch ?
<ul>{this.state.jokes.map(item => <li key={item.id}>{item.joke}</li>)}
</ul> : <p className="random-joke">{this.state.joke}</p>}
</div>
);
}
render() {
return (
<div>
<h1>Dad Jokes</h1>
<RetrievalForm
onFormSubmit={this.searchJokes}
onInputChange={this.onSearchChange}
isSearching={this.state.isFetchingJokes}
onRandomize={this.randomizeJokes}
/>
{this.state.isFetchingJokes ? <p className="searching-message">Searching for jokes...</p> : this.jokeRender()}
</div>
);
};
}
Child component:
const RetrievalForm = props => {
const onSubmit = e => {
// Prevents GET request/page refresh on submit
e.preventDefault();
props.onFormSubmit();
};
return (
<>
<form onSubmit={onSubmit}>
<input
type="text"
placeholder="Enter search term..."
onChange={e => props.onInputChange(e.target.value)}
required
/>
<div>
{/* Specifying type here since it's good practice; different browsers may use default types for buttons */}
<button type="submit" disabled={props.isSearching}>Search</button>
{/* type="button" stops input validation message from being displayed (on Firefox) when randomize button is clicked without anything entered */}
<button type="button" onClick={props.onRandomize} disabled={props.isSearching} className="randomize-button">
Randomize
</button>
</div>
</form>
</>
);
};
Any help would be greatly appreciated.
You need to pass your searchTerm down to the RetrievalForm and in that input set value={searchTerm} so that it's value will be bound to that state.
Basically, you need to store the input value in the component's state. When onSubmit is called, we should revert that value to an empty string.
Example with some React Hooks goodness:
import React, { Component, useState } from 'react';
const RetrievalForm = props => {
const [searchTerm, setSearchTerm] = useState('');
const onChange = e => {
const { value } = e.target;
props.onInputChange(value);
setSearchTerm(value)
}
const onSubmit = e => {
// Prevents GET request/page refresh on submit
e.preventDefault();
props.onFormSubmit();
setSearchTerm('');
};
return (
<>
<form onSubmit={onSubmit}>
<input
type="text"
value={searchTerm}
placeholder="Enter search term..."
onChange={onChange}
required
/>
<div>
{/* Specifying type here since it's good practice; different browsers may use default types for buttons */}
<button type="submit" disabled={props.isSearching}>
Search
</button>
{/* type="button" stops input validation message from being displayed (on Firefox) when randomize button is clicked without anything entered */}
<button type="button" onClick={props.onRandomize} disabled={props.isSearching} className="randomize-button">
Randomize
</button>
</div>
</form>
</>
);
};
Example here: https://stackblitz.com/edit/react-db5ire
As part of a technical test, I've been asked to write an autocomplete input in React. I've done this but I'd now like to add the functionality of navigating up and down the rendered list with the arrow keys. I've done some extensive Googling and found nothing React specific apart from npm packages.
To be clear, I'm looking for something like this but for React: https://www.w3schools.com/howto/howto_js_autocomplete.asp
All I basically need is the arrow button functionality, I've got everything else working fine.
Cheers
Here's an example that I tried but couldn't get working.
export default class Example extends Component {
constructor(props) {
super(props)
this.handleKeyDown = this.handleKeyDown.bind(this)
this.state = {
cursor: 0,
result: []
}
}
handleKeyDown(e) {
const { cursor, result } = this.state
// arrow up/down button should select next/previous list element
if (e.keyCode === 38 && cursor > 0) {
this.setState( prevState => ({
cursor: prevState.cursor - 1
}))
} else if (e.keyCode === 40 && cursor < result.length - 1) {
this.setState( prevState => ({
cursor: prevState.cursor + 1
}))
}
}
render() {
const { cursor } = this.state
return (
<Container>
<Input onKeyDown={ this.handleKeyDown }/>
<List>
{
result.map((item, i) => (
<List.Item
key={ item._id }
className={cursor === i ? 'active' : null}
>
<span>{ item.title }</span>
</List.Item>
))
}
</List>
</Container>
)
}
}
And here is my code:
class Search extends Component {
constructor(props) {
super(props);
this.state = {
location: '',
searchName: '',
showSearch: false,
cursor: 0
};
}
handleKeyPress = e => {
const { cursor, searchName } = this.state;
// arrow up/down button should select next/previous list element
if (e.keyCode === 38 && cursor > 0) {
this.setState(prevState => ({
cursor: prevState.cursor - 1
}));
} else if (e.keyCode === 40 && cursor < searchName.length - 1) {
this.setState(prevState => ({
cursor: prevState.cursor + 1
}));
}
};
render() {
const { searchName, location } = this.state;
return (
<div className="Search">
<h1>Where are you going?</h1>
<form id="search-form" onSubmit={this.handleSubmit}>
<label htmlFor="location">Pick-up Location</label>
<input
type="text"
id="location"
value={location}
placeholder="city, airport, station, region, district..."
onChange={this.handleChange}
onKeyUp={this.handleKeyUp}
onKeyDown={this.handleKeyPress}
/>
{this.state.showSearch ? (
<Suggestions searchName={searchName} />
) : null}
<button value="submit" type="submit" id="search-button">
Search
</button>
</form>
</div>
);
}
Code that renders the list from the restful API:
.then(res =>
this.setState({
searchName: res.data.results.docs.map(array => (
<a href="#">
<div
key={array.ufi}
className="locations"
>
{array.name}
</div>
</a>
))
})
);
Since you are defining a function as handleKeyDown(e) {...} the context this is not pointing to the context of class instance, the context will be supplied by onKeyDown (and I suppose it's window as this)
So, you have 2 ways to go:
declare your function as handleKeyDown = (e) => {...}
bind handleKeyDown context to component instance like onKeyDown={this.handleKeyDown.bind(this)}
Also, don't forget that you may want a mod items.length counter, meaning when your press down and the last item is already selected, it would go to the first item.
Your api usage with storing markdown into the state is just a terrible thing to do. You don't have access to these strings anymore, instead save it as a plain array. Pass it where you need it, and use it to create jsx there.
Also, you don't use cursor from your state at all.
I have a map that render few items and one of its line is below
<a onClick={()=> this.setState({"openDeleteModal":true)}>Delete</a>
Obviously I want to open a modal when user click the delete, but I have to pass a few things like the name of the item, id of the item to perform the deletion. How can I pass says the name to the modal?
I can bind the obj name to a like this
Delete
Am I on the right track?
When working on React applications, try not to think in terms of passing values to other components, but rather updating state that your components are exposed to.
In your example, assuming your modal component is a child of the same component your list of a tags belongs to, you could set the values you are interested in exposing to the modal on the state, as well as updating the property that signals whether the modal is open or not. For example:
class Container extends React.Component {
constructor(props) {
super(props)
this.state = {
openDeleteModal: false,
activeItemName: '', //state property to hold item name
activeItemId: null, //state property to hold item id
}
}
openModalWithItem(item) {
this.setState({
openDeleteModal: true,
activeItemName: item.name,
activeItemId: item.id
})
}
render() {
let buttonList = this.props.item.map( item => {
return (<button onClick={() => this.openModalWithItem(item)}>{item.name}</button>
});
return (
<div>
{/* Example Modal Component */}
<Modal isOpen={this.state.openDeleteModal}
itemId={this.state.activeItemId}
itemName={this.state.activeItemName}/>
{ buttonList }
</div>
)
}
}
Copying over my answer from How to pass props to a modal
Similar scenario
constructor(props) {
super(props)
this.state = {
isModalOpen: false,
modalProduct: undefined,
}
}
//****************************************************************************/
render() {
return (
<h4> Bag </h4>
{this.state.isModalOpen & (
<Modal
modalProduct={this.state.modalProduct}
closeModal={() => this.setState({ isModalOpen: false, modalProduct: undefined})
deleteProduct={ ... }
/>
)
{bag.products.map((product, index) => (
<div key={index}>
<div>{product.name}</div>
<div>£{product.price}</div>
<div>
<span> Quantity:{product.quantity} </span>
<button onClick={() => this.props.incrementQuantity(product, product.quantity += 1)}> + </button>
<button onClick={() => this.decrementQuantity(product)}> - </button> // <----
</div>
</div>
))}
)
}
//****************************************************************************/
decrementQuantity(product) {
if(product.quantity === 1) {
this.setState({ isModalOpen: true, modalProduct: product })
} else {
this.props.decrementQuantity(product)
}
}
Try this: this is the form which has the button, and is a child component of some other component that passes the handleButtonAction method as props, and the button takes the input data and invokes this parent component method
handleSubmit = (e) => {
e.preventDefault();
const data = e.target.elements.option.value.trim();
if (!data) {
this.setState(() => ({ error: 'Please type data' }));
} else {
this.props.handleButtonAction(data, date);
}
}
{this.state.error && <p>{this.state.error}</p>}
<form onSubmit={this.handleSubmit}>
<input type="text" name="option"/>
<div>
<button>Get data</button>
</div>
</form>
The parent component:
handleButtonAction = (data) => {
axios.get(`http://localhost:3000/someGetMethod/${data}`).then(response => {
const resData = response.data;
this.setState({
openModal: true,
status: response.status,
data: resData
});
}).catch((error) => {
if (error.message.toLowerCase() === 'network error') {
this.setStateWithError(-1, {});
}
else { // not found aka 404
this.setStateWithError(error.response.status, '', {currency, date: ddat});
}
});
}