I'm very new to React. I'm building a flight booking website. I want to hide the return date form field when the user selects one way trip. I wanted to know what is the best way to go about it.
const TripTypeButton = (props) => {
const [rSelected, setRSelected] = useState(null);
return (
<div>
<ButtonGroup>
<Button color="primary" onClick={() => setRSelected(1)} active={rSelected === 1}>Round Trip</Button>
<Button color="secondary" onClick={() => setRSelected(2)} active={rSelected === 2}>One Way</Button>
</ButtonGroup>
</div>
);
}
const HomePage = (props) =>{
return(
<div>
<div>
<h2> Search for flights</h2>
</div>
<div>
<Form>
<Row form>
<FormGroup>
<TripTypeButton />
</FormGroup>
</Row>
<Row form>
<Col md={3}>
<FormGroup>
<Label>Departure</Label>
<Input type="date" id="departure" name="departure"/>
</FormGroup>
</Col>
<Col md={3}>
<FormGroup>
<Label for="exampleState">Return</Label>
<Input type="date" name="return" id="return"/>
</FormGroup>
</Col>
<Row/>
</Form>
</div>
</div>
);
}
for this situation, you need to keep the state of selected trip type in HomePage component and then based on that state render the Return flight or not!
like below:
const HomePage = (props) => {
const [rSelected, setRSelected] = useState(null);
return (
// some code
// trip type button handler
<TripTypeButton handleSelectedTripType={setRSelected} rSelected={rSelected} />
// the button section of code
{ rSelected !== 1
&& <Col md={3}>
<FormGroup>
<Label for="exampleState">Return</Label>
<Input type="date" name="return" id="return"/>
</FormGroup>
</Col>
}
// rest of jsx code
)
}
and your TripTypeButton would be like below:
const TripTypeButton = ({ handleSelectedTripType, rSelected }) => {
return (
<div>
<ButtonGroup>
<Button color="primary" onClick={() => handleSelectedTripType(1)} active={rSelected === 1}>Round Trip</Button>
<Button color="secondary" onClick={() => handleSelectedTripType(2)} active={rSelected === 2}>One Way</Button>
</ButtonGroup>
</div>
);
}
this is called lifting state up in React! and by that you keep your state in top level components and manage data manipulation by passing required handlers down to the child components by directly props or context!
Related
I have a this code:
<Row>
{teamMemberForm.timetables.filter((timeframe) => timeframe.day === props.day).map((timeframe) => {
return (
<React.Fragment>
<Col md={5}>
<Label>
<strong>{"De"}</strong>
</Label>
<input
className={"form-control"}
type="time"
id={`timeslot-from-${timeframe.key}`}
name={`timeslot-from-${timeframe.key}`}
defaultValue={timeframe.start_date}
min="09:00"
max="18:00"
onChange={(ev) => handleTimeFrameFrom(ev, timeframe.key)}
required
></input>
</Col>
<Col md={5}>
<Label>
<strong>{"A"}</strong>
</Label>
<input
className={"form-control"}
type="time"
id={`timeslot-to-${timeframe.key}`}
name={`timeslot-to-${timeframe.key}`}
defaultValue={timeframe.end_date}
onChange={(ev) => handleTimeFrameTo(ev, timeframe.key)}
min="09:00"
max="18:00"
required
></input>
</Col>
<Col md={2}>
<CloseIcon
className={classes.closeButton}
onClick={() => handleRemoveTimeFrame(timeframe.key)}
/>
<p>{timeframe.key}</p>
{console.log(
teamMemberForm.timetables.filter(
(timeframe) => timeframe.day === props.day
)
)}
</Col>
</React.Fragment>
);
})}
</Row>
When pushing CloseIcon to trigger handleRemoveTimeFrame it performs well, it removes the desired timeframe, the problem is, in the DOM always the last element of the array gets removed (although in reality, what gets removed is the object I want to be removed, and if I re-render the component it shows correctly)
How can I do, so when I push CloseIcon what disappears is the actual element removed and not the last element. (In other words, how do I do for it to re-render each time I trigger handleRemoveTimeFrame
This is the content of handleRemoveTimeFrame;
const handleRemoveTimeFrame = (key_to_remove) => {
dispatch(
setTeamMemberForm({
...teamMemberForm,
timetables: teamMemberForm.timetables.filter((timeFrame) => {
return timeFrame.key !== key_to_remove;
}),
})
);
};
It is removing an object within an array stored in redux, and I verified it is working fine.
From the parent component. There are other forms, hence, there is a conditional form where it will used the child component's form.
A user may click the button to show the form from the child component.
const [isTrue, setIsTrue] = useState(false);
<ButtonForm onClick={() => setIsTrue(true)}>
Click for the Form
</ButtonForm>
{isTrue == true ? (
<Form
data={entryData}
items={items}
names={names}
/>
) : (
></>
)}
This is the child component. The problem here is that, everytime I'll submit it, it will reload the page:
const Form = ({ entryData, items, names }) => {
const handleSubmit = (e) => {
e.preventDefault();
try {
console.log(" saved");
} catch (err) {
console.log(err);
}
};
return (
<>
<form onSubmit={handleSubmit}>
//some codes here
<Grid item>
<TextField
type="text"
label="Item Number"
variant="outlined"
fullWidth
required
/>
</Grid>
//some codes here
<Grid item>
<Button type="submit" fullWidth>
Submit
</Button>
</Grid>
</form>
<br /> <br />
</>
);
};
export default Form;
This is the sample parent component: most where just forms
<div className="App">
<Card className={classes.root}>
<CardContent>
{users &&
users.map((user) => (
<li style={{ listStyle: "none" }}>
<Button onClick={() => setIsTrue(true)}>
Click for the Form
</Button>
{//if user condition is true here == true ? (
<div>
//form here with the textfields
</div>
) : (
<div>
//form here with the textfields
</div>
)}
</li>
))}
</>
{isTrue == true ? (
<Form
data={entryData}
items={items}
names={names}
/>
) : (
></>
)}
</CardContent>
</Card>
</div>
// on click trigger submit in first and second forms
<form>
<form>
<button type="submit" />
</form>
<button type="submit" />
</form>;
//solution
<form onSubmit={(e) => e.preventDefault()}>
<form onSubmit={(e) => e.preventDefault()}>
<button type="submit" /> // on click trigger submit in first and second form
</form>
<button type="submit" />
</form>;
I'm fairly new to react, and I got involved in this project that was already ongoing, and I was asked to add more fields to a form, after the user chooses how many they want. Basically the form has a couple of fields, and if the user wants to add more people they can choose 1, 2, 3 or 4 and I need them to do so when choosing from a drop down menu, but I'm not sure how to do it. Can I make a div hide or not based on the click from the drop down? here is the code I'm working with.
It could also be one button that adds multiple fields on click one or the other
I've seen a couple of people doing it with redux, but I don't know enough of redux to figure out what they were doing.
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import {
Card, CardBody, Col, Button, Form, FormGroup, Label, Input, UncontrolledDropdown,
DropdownToggle, DropdownMenu, DropdownItem,
} from 'reactstrap';
import ChevronDownIcon from 'mdi-react/ChevronDownIcon';
import PropTypes from 'prop-types';
import StaticBar from './StaticBar';
import { getBasicQuote } from '../../../redux/actions/quoteActions';
import QuotePanel from './QuotePanel';
class VerticalForm extends PureComponent {
static propTypes = {
history: PropTypes.shape({}).isRequired,
getBasicQuotethis: PropTypes.func.isRequired,
};
constructor() {
super();
this.state = {
};
}
handleSubmit = async (event) => {
const { getBasicQuotethis } = this.props;
const gender = event.target.elements.gender.value;
const age = event.target.elements.age.value;
event.preventDefault();
getBasicQuotethis(age, gender);
}
render() {
return (
<>
<Col md={12} lg={12}>
<Card>
<CardBody>
<div className="card__title">
<h5 className="bold-text">Quote Tool</h5>
<h5 className="subhead">Labels are above fields</h5>
</div>
<Form className="form" onSubmit={this.handleSubmit}>
<FormGroup className="form__form-group">
<Label className="form__form-group-label" for="registerEmail">Gender</Label>
<Input
// className="form__form-group-field"
type="text"
name="gender"
id="registerEmail"
placeholder="Gender"
/>
</FormGroup>
<FormGroup className="form__form-group">
<Label className="form__form-group-label" for="registerPassword">Age</Label>
<Input
// className="form__form-group-field"
type="text"
name="age"
id="registerEmail"
placeholder="Age"
/>
</FormGroup>
<Button size="sm" className="btn btn-primary" type="Submit">
Submit
</Button>
<UncontrolledDropdown>
<DropdownToggle className="icon icon--right" outline color="primary">
<p>Add Dependent <ChevronDownIcon /></p>
</DropdownToggle>
<DropdownMenu className="dropdown__menu">
<DropdownItem>1</DropdownItem>
<DropdownItem>2</DropdownItem>
<DropdownItem>3</DropdownItem>
<DropdownItem divider />
<DropdownItem>4 +</DropdownItem>
</DropdownMenu>
</UncontrolledDropdown>
</Form>
</CardBody>
</Card>
</Col>
<StaticBar />
<QuotePanel />
</>
);
}
}
export default connect(null, { getBasicQuotethis: getBasicQuote })(VerticalForm);
Thank you for your help
Ok so I figure it out how to do it.
class VerticalForm extends PureComponent {
static propTypes = {
history: PropTypes.shape({}).isRequired,
getBasicQuotethis: PropTypes.func.isRequired,
};
constructor() {
super();
this.state = {
showHideDependentSpouse: false,
showHideDependent2: false,
showHideDependent3: false,
showHideDependent4: false,
};
this.hideComponent = this.hideComponent.bind(this);
}
handleSubmit = async (event) => {
const { getBasicQuotethis } = this.props;
const gender = event.target.elements.gender.value;
const age = event.target.elements.age.value;
event.preventDefault();
getBasicQuotethis(age, gender);
}
hideComponent(name) {
switch (name) {
case 'showHideDependentSpouse':
this.setState(prevState => ({ showHideDependentSpouse: !prevState.showHideDependentSpouse }));
break;
case 'showHideDependent2':
this.setState(prevState => ({ showHideDependent2: !prevState.showHideDependent2 }));
break;
case 'showHideDependent3':
this.setState(prevState => ({ showHideDependent3: !prevState.showHideDependent3 }));
break;
case 'showHideDependent4':
this.setState(prevState => ({ showHideDependent4: !prevState.showHideDependent4 }));
break;
default:
}
}
render() {
const {
showHideDependentSpouse, showHideDependent2, showHideDependent3, showHideDependent4,
} = this.state;
return (
<>
<Col md={12} lg={12}>
<Card>
<CardBody>
<div className="card__title">
<h5 className="bold-text">Quote Tool</h5>
<h5 className="subhead">Labels are above fields</h5>
</div>
<Form className="form" onSubmit={this.handleSubmit}>
<FormGroup className="form__form-group">
<Label className="form__form-group-label" for="registerEmail">Gender</Label>
<Input
// className="form__form-group-field"
type="text"
name="gender"
id="registerEmail"
placeholder="Gender"
/>
</FormGroup>
<FormGroup className="form__form-group">
<Label className="form__form-group-label" for="registerPassword">Age</Label>
<Input
// className="form__form-group-field"
type="text"
name="age"
id="registerEmail"
placeholder="Age"
/>
<div>
{showHideDependentSpouse && <DependentSpouse />}
{showHideDependent2 && <Dependent1 />}
{showHideDependent3 && <Dependent1 />}
{showHideDependent4 && <Dependent4 />}
</div>
</FormGroup>
<Button size="sm" className="btn btn-primary" type="Submit">
Submit
</Button>
<Row>
<Col lg={8}>
<p>Check each box to add the respective number of dependents</p>
</Col>
<Col>
<Label check>
<Input type="checkbox" onClick={() => this.hideComponent('showHideDependentSpouse')} />
</Label>
</Col>
<Col>
<Label check>
<Input type="checkbox" onClick={() => this.hideComponent('showHideDependent2')} />
</Label>
</Col>
<Col>
<Label check>
<Input type="checkbox" onClick={() => this.hideComponent('showHideDependent3')} />
</Label>
</Col>
<Col>
<Label check>
<Input type="checkbox" onClick={() => this.hideComponent('showHideDependent4')} />
</Label>
</Col>
</Row>
</Form>
</CardBody>
</Card>
</Col>
<StaticBar />
<QuotePanel />
</>
);
}
}
export default connect(null, { getBasicQuotethis: getBasicQuote })(VerticalForm);
Basically set the state for each of the components i want to render, made individual components aside, and then made a logic to change the state false or true, then created a condition on each to check if the state is true.
Hopefully this will be helpful for someone.
I would like to delete a search result by clicking on the X icon on the individual card.
The search returns 10 recipes from the API, generating 10 divs. How would I go about removing individual divs onClick of the icon whilst keeping the other divs? Essentially just a remove search result button.
return (
<div className='App'>
<form onSubmit={getSearch} className="search-form">
<InputGroup>
<InputGroupAddon addonType="prepend">
<InputGroupText><FontAwesomeIcon icon={faSearch} /></InputGroupText>
</InputGroupAddon>
<Input className="search-bar" type="text" placeholder="Search for recipe..." value={search} onChange={updateSearch} />
</InputGroup>
<Button color="primary" size="sm" className="search-button" type="submit">Search</Button>
</form>
<div className="recipes">
{recipes.map(recipe => (
<Recipe
key={recipe.recipe.label}
title={recipe.recipe.label}
theUrl={recipe.recipe.url}
image={recipe.recipe.image}
ingredients={recipe.recipe.ingredients}
source={recipe.recipe.source}
healthLabels={recipe.recipe.healthLabels}
servings={recipe.recipe.yield} />
))}
</div>
</div>
const Recipe = ({ title, theUrl, image, ingredients, source, healthLabels, servings }) => {
return (
<div className={style.recipe}>
<FontAwesomeIcon className={style.delete} icon={faTimes} />
<h3 >{title}</h3>
<Badge className={style.badge} color="primary">{source}</Badge>
<p>Serves: <Badge color="primary" pill>{servings}</Badge></p>
<img src={image} alt='food' />
<ol className={style.allergens}>
{healthLabels.map(healthLabel => (
<li>{healthLabel}</li>
))}
</ol>
<div className={style.ingr}>
<ol>
{ingredients.map(ingredient => (
<li>{ingredient.text}</li>
))}
</ol>
<Button className={style.button} outline color="primary" size="sm" href={theUrl} target="_blank">Method</Button>
</div>
<div className={style.info}>
<div className={style.share}>
<WhatsappShareButton url={theUrl}><WhatsappIcon round={true} size={20} /></WhatsappShareButton>
<FacebookShareButton url={theUrl}><FacebookIcon round={true} size={20} /></FacebookShareButton>
<EmailShareButton url={theUrl}><EmailIcon round={true} size={20} /></EmailShareButton>
</div>
</div>
</div>
);
}
Simply update the recipes array and React will update the HTML.
I'm not sure where recipes comes from, but if you set an onClick on, say, <Recipe label="x"> that deletes the corresponding recipe element from recipes, then React should no longer render that recipe.
That should be easy. Here's one way:
add onClick to this
<FontAwesomeIcon onClick={deleteRecipe} className={style.delete} icon={faTimes} />
pass a reference of the function that deletes the recipe.
deleteRecipeHandler = (id) => {
// filter your recipes here such that the new recipes array doesn't contain the recipe
// with the id you're getting here.
// Change the below code how you need
const newRecipes = oldRecipes.filter(recipe => {
return recipe.id !== id;
});
}
{recipes.map(recipe => (
<Recipe
key={recipe.recipe.label}
deleteRecipe={this.deleteRecipeHandler.bind(this,recipe.recipe.id)}
title={recipe.recipe.label}
theUrl={recipe.recipe.url}
image={recipe.recipe.image}
ingredients={recipe.recipe.ingredients}
source={recipe.recipe.source}
healthLabels={recipe.recipe.healthLabels}
servings={recipe.recipe.yield} />
))}
since you're destructuring your props you can use
const Recipe = ({ title, theUrl, image, ingredients, source, healthLabels, servings, deleteRecipe }) => {
return (
<div className={style.recipe}>
<FontAwesomeIcon onClick={deleteRecipe} className={style.delete} icon={faTimes} />
I'm trying to pass information from one component to another via props in ReactJS. I'm trying to access the inside of an array that has a string of information (picture included), and then I'm trying to get that information accessible to my return() function in the component so I can use it in a modal.
This is the component that I passed the props into:
class OfferActionPreviewModal extends Component {
constructor(props) {
super(props);
this.state = {
success: false,
action: [],
offerActions: []
};
this.toggleSuccess = this.toggleSuccess.bind(this);
}
toggleSuccess() {
this.setState({
success: !this.state.success
});
}
componentDidMount() {
this.renderAdvertiserForm();
}
renderAdvertiserForm = () => {
const {
offerAction,
offerActionTriggers,
triggers,
offer
} = this.props;
console.log(offerActionTriggers);
return (
<form>
<ModalHeader toggle={this.toggleSuccess}>
{offerAction.name} - <em>{offerAction.id}</em>
</ModalHeader>
<ModalBody>
<div>
<Row>
<Col lg={6}>
<Label>Post Name</Label>
<Field
name='offerActions.name'
type='text'
component={renderField}
/>
</Col>
<Col lg={6}>
<Label>Post Method</Label>
{offerAction.postMethod}
</Col>
</Row>
<br />
<Row>
<Col lg={12}>
<Label>URL</Label>
{offerAction.url}
</Col>
</Row>
<br />
</div>
<br />
</ModalBody>
</form>
);
};
render() {
return (
<div className='animated'>
<Modal
isOpen={this.state.success}
toggle={this.toggleSuccess}
className='modal-info'
>
{this.renderAdvertiserForm()}
</Modal>
</div>
);
}
}
export default connect()(OfferActionPreviewModal);
I'm trying to access inside the OfferActionTriggers, to the OfferActionCriteria part of the object, and I've included an image of the object for reference.
Thanks for all help in advance.
You can use Nested Object Destructuring for this purpose like this
const {offerAction:{OfferActionCriteria,offerActionPost:{url,name,id,postMethod}}} =
this.props; to access the OfferActionCriteria object. inside renderAdvertiserForm function.
renderAdvertiserForm = () => {
const {offerAction:{OfferActionCriteria,offerActionPost:{url,name,id,postMethod}}} = this.props;
console.log(OfferActionCriteria);
return (
<form>
<ModalHeader toggle={this.toggleSuccess}>
{name} - <em>{id}</em>
</ModalHeader>
<ModalBody>
<div>
<Row>
<Col lg={6}>
<Label>Post Name</Label>
<Field
name='name'
type='text'
component={renderField}
/>
</Col>
<Col lg={6}>
<Label>Post Method</Label>
{postMethod}
</Col>
</Row>
<br />
<Row>
<Col lg={12}>
<Label>URL</Label>
{offerAction.url}
</Col>
</Row>
<br />
</div>
<br />
</ModalBody>
</form>
);
};