How to get selected data from child state to parent in React - javascript

I have a flight page with various cards with different route information in each. Within each card I have a select button which is identified by the flightID.
Select(FD) {
this.state={route:''};
return (
<div>
{FD.FlightID}
<label>
<Checkbox id={FD.FlightID}
name={FD.FlightID}
checked={this.setState.route}
onChange={this.handleCheckboxChange}
/>
<span>Select</span>
</label>
</div>
)
}
The Select method is called within each card.
return data.map( FD1 => (
<Row>
<Card className="card">
<Card body className="text-center">
<CardTitle data-es-label="location"> Location:
{FD1.Departure}
</CardTitle>
<CardText data-es-label="Price">Price
{FD1.price}
</CardText>
{this.Select(FD1)}
<CardActions>'
How do I access the data of the specific card selected to send to make a booking?
EDIT:
the handleCheckBoxChange
handleCheckboxChange = event =>
this.setState({ route: event.target.checked });

One way to do this is to pass id in onChange method like this
<Checkbox
...
onChange={() => this.handleCheckboxChange(FD.FlightID)}
/>
Then in parent component you can get your element from data array like this
handleCheckboxChange = (id) => {
const selected = data.find(e => e.FlightID = id);
...
}
If you want to pass some data through more components you need to read about React context (https://reactjs.org/docs/context.html)

try this.
<Checkbox
id={FD.FlightID}
name={FD.FlightID}
checked={this.setState.route}
onChange={() => this.handleCheckboxChange(FD)}
/>

Related

How to use onClick event on Card.js component to render Article.js Component in React?

Right now i am in Home.js page and i want to render Article.js component/page when user click on particular card (Card.js component). Here is my Home.js code
const Home = () => {
const posts = useSelector((state) => state.posts)
const [currentId, setCurrentId] = useState(null)
const handleClick = () => {
return <Article />
}
return (
<div className="container">
<h4 className="page-heading">LATEST</h4>
<div className="card-container">
{
posts.map(post => <Card key={post._id} post={post} setCurrentId={setCurrentId} onClick={handleClick} />)
}
</div>
</div>
)
}
ONE MORE PROBLEM :
How can I send post variable into onClick method? when i send it method is getting called.
Thank You in Advance :)
It sounds like you want to use the React Router? As I take it you want to load the post as its own page?
I should also point out that any function passed to onClick cannot return anything. The only purpose return can serve in an event function is to exit the function early.
I do agree with #Jackson that you might want to to look into React Router. But you don't need it. You can conditionally render the Article component based on the currentId.
A click handler shouldn't return anything. Instead of returning the <Article /> from the onClick callback, you would use onClick to control the currentId state. You can pass a function that sets the currentId to the post id based on the post variable in your map like this: onClick={() => setCurrentId(post._id)}.
The return for your Home component will either render the list of posts or a current post, depending on whether or not you have a currentId or just null.
const Home = () => {
const posts = useSelector((state) => state.posts);
const [currentId, setCurrentId] = useState(null);
return (
<div className="container">
{currentId === null ? (
// content for list of posts - when currentId is null
<>
<h4 className="page-heading">LATEST</h4>
<div className="card-container">
{posts.map((post) => (
<Card
key={post._id}
post={post}
// arrow function takes no arguments but calls `setCurrentId` with this post's id
onClick={() => setCurrentId(post._id)}
/>
))}
</div>
</>
) : (
// content for a single post - when currentId has a value
<>
<div
// setting currentId to null exits the aritcle view
onClick={() => setCurrentId(null)}
>
Back
</div>
<Article
// could pass the whole post
post={posts.find((post) => post._id === currentId)}
// or could just pass the id and `useSelector` in the Article component to select the post from redux
id={currentId}
// can pass a close callback to the component so it can implement its own Back button
onClickBack={() => setCurrentId(null)}
/>
</>
)}
</div>
);
};
To pass in the click hadler the params you want, one could do something like this:
posts.map(post =>
<Card
key={post._id}
post={post}
onClick={() => handleClick(post)} />
)

React: Edit and Delete from a pop-up menu on a list item

I have a list container component, which is the parent. It maps out the list rows. On the list row component, which is the child, every item has a button to toggle a pop-up menu, which has a button for edit, and a button for delete. The menu itself is a sibling to the list rows because if I include it in the list rows component, each row will render a menu and when toggled, they would all stack up on top of each other. The edit and delete buttons toggle either a form for the edit, or directly remove the item.
What I currently have is:
// Parent / Container
const [itemID, setItemID] = useState(null);
const handleMenuOpen = (id) => (e) => {
setAnchorEl(e.currentTarget); // for menu placement
setItemID(id);
};
const handleItemDelete = () => {
dispatch(deleteItem(itemID));
};
<List>
<ListRow handleMenuOpen={handleMenuOpen} />
<Menu handleItemDelete={handleItemDelete} itemID={itemID} />
</List>;
// List Row
<Button onClick={handleMenuOpen(item.id)} />;
// Menu
<MenuItem onClick={() => handleModalOpen(itemID)} />;
<MenuItem onClick={() => handleItemDelete()} />;
The edit button works fine but no matter how I try, I cannot get setItemID to work from the onClick on the list item. It always come out as the initial value of null. I console logged that the ID in the function parameter came out properly but the setState hook did not work.
I tried putting the useState on the list item and pass the ID through useContext but came out undefined when handledItemDelete was called.
I tried using ref on the child to get the ID from the parent, which also came out as undefined.
I cannot think of how to use useEffect to check for a change in the handleMenuOpen parameter.
I am out of ideas. Anyone know what the issue is and how to fix it?
You should probably just pass the handleMenuOpen function and rely on the selected element and then store it's id in itemID variable.
const handleMenuOpen = (e) => {
setAnchorEl(e.currentTarget); // for menu placement
setItemID(e.currentTarget.id);
};
<MenuItem onClick={handleMenuOpen} />;
i had the same problem before. I think you should handle the popup toggling in the child component, so something like this.
function Parent() {
function handleDelete(item) {
deleteFunction(item.id)
}
return (
<div>
{[].map((item, index) => {
return (
<ListRowItem key={index} handleDelete={handleDelete} item={item} />
)
})}
</div>
)
}
function ListRowItem({handleDelete, item}) {
const [isMenuOpen, setIsMenuOpen] = useState(false)
const [isModelVisible, setIsModalVisible] = useState(false)
return (
<div>
<Button onClick={isMenuOpen === true ? () => setIsMenuOpen(true) : () => setIsMenuOpen(false)} />
{isModelVisible === true ? <ModalItem /> :null}
{isMenuOpen === true ?
<div>
<MenuItem onClick={() => setIsModalVisible(true)} />
<MenuItem onClick={() => handleDelete(item.id)} />
</div>
: null}
</div>
)
}
I assume you are doing a certain loop to render each List Row inside the List component
let's say all items are in an items array which you loop:
{items.map(item => (
<ListRow handleMenuOpen={handleMenuOpen}/>
<Menu handleItemDelete={handleItemDelete} item={item} />
)}
now in the Menu container or component, you would have the item and pass it to the Menu item

Could not console log the updated state array

I am making a simple application where the user can create a new form on a button click, so for this, I have an array state like this :
const [numbers, setNumbers] = useState([0]);
const [count, setCount] = useState([0]);
And on my button onClick method I have this,
setCount(count + 1);
setNumbers(numbers.concat(numbers[0] + count));
In my render method, I have :
{numbers.map((number) => {
return (
<div key={number}>
<InputCreator id={number} value={number} />
</div>
);
})}
And my InputCreator component is a simple callback component with few textfields.
So far, it works well. Lately I wanted to add a delete functionality where the user can delete that particular form. So, I added.a button inside this form and on the onClick method, I tried console loging the "numbers" state to check everything is working, but it logs only the default value I have given while creating the state and not the updated state. What could be the reason ? So my idea is to delete that index from the array using the props passed, so that the component will re-render with the updated number of forms. Is there a better way to do this ?
EDIT : This is my InputCreator component,
const InputCreator = useCallback((props) => {
const { id, value } = props;
// console.log(titles[id]);
return (
<div>
<Col xs={12} md={8}>
<div className={styles.container}>
<Form
noValidate
validated={false}
onSubmit={handleSubmit}
encType="multipart/form-data"
>
<Form.Group controlId="formGroupTitle">
<Form.Label>Title</Form.Label>
<Form.Control
type="text"
placeholder="Title"
onChange={(e) => handleChange(e, value)}
name="title"
value={titles[id]}
/>
</Form.Group>
<Form.Group controlId="formGroupTitle">
<Form.Label>Description</Form.Label>
<Form.Control
type="text"
name="description"
placeholder="Max limit 30"
onChange={(e) => handleChange(e, value)}
maxLength={31}
value={descriptions[id]}
/>
</Form.Group>
<Button
variant="outline-primary"
size="sm"
className={styles.deleteBtn}
onClick={(e) => handleDelete(e, number)}
>
X
</Button>
</Form>
)})
handleDelete :
const handleDelete = (e, value) => {
e.preventDefault();
console.log(numbers);
}
I will just point out the mistakes I see as your question is not worded clearly.
Keys aren't passed into they are passed into list in order to create a list for every element with a unique ID. I am not sure what you are trying to do in your render method but I would suggest doing something like this :
const renderNumbers = () => myArray.map((number, index) =>
<MyComponent key={index} number={number} />
To delete an element. Create a function that takes in the ID. Filter it to show a new array without that ID, below is an example that you ca apply.
const deletePerson = (id) => {
setNumbers(numbers.filter((number) => number.id !== id));
};
Send this handle to the button that deletes the element
const handleDeleteNumber = () => {
deleteNumber(number.id);
};
Make a single handleInputChange() that you can use for all input changes. Such as this one :
const handleInputChange = (event) => {
setInfo({ ...info, [event.target.name]: event.target.value });
};
In the tag you pass in id and values separately like this
value={descriptions}
key={id}

(React) How to make checkbox UI look checked after mutating state?

How to change the UI of the CheckBox component? State mutates finely as I wanted, however the checkbox doesn't look checked after the onCheck call (when click it).
I guess I must create or use an existing props for input objects:
onCheck = item => {
this.setState(prevState => ({
usage: prevState.usage.concat(item.label, ",")
}));
//Todo: Display as checked
};
{checkboxes.map(item => (
<div className="d-inline-block w-50 p">
<CheckBox
name={item.name}
onChange={this.onCheck.bind(this, item)}
/>
<label key={item.key}>{item.label}</label>
</div>
))}
const CheckBox = ({ type = "checkbox", name, checked = false, onChange }) => (
<input type={type} name={name} checked={checked} onChange={onChange} />
);
Welcome to the community, you need to pass checked true/false to the CheckBox component
<CheckBox
name={item.name}
checked={this.state.usage.includes(item.label)}
onChange={this.onCheck.bind(this, item)}
/>
Also you don't need to include ',' while concat as it is creating an array
Refer link for reference https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat
You need to include the checked property and you can use a state to handle it. Let's say "isChecked".
Example:
<CheckBox checked={this.state.isChecked} \> (+ your previously added properties)
And you'd include something like this in your function:
this.setState((prevState) => { isChecked: !prevState.isChecked});

How to filter data, using checkboxes, without using redux

I am using React to display book titles that I want filtered by category. I want the titles filtered once a checkbox next to the category name is clicked. I am not using a submit button.
I am somewhat new to React and read the documentation about "lifting state," but I haven't been able to get that to work. I have not yet read the Hooks or Context API documentation. Perhaps that's the solution, but it seems what I'm doing isn't complex enough for that...maybe not?
class Checkbox extends Component {
state = {
checked: false
}
handleClick = (e) => {
this.setState(() => ({ checked: !this.state.checked }))
}
render() {
const name = this.props.name;
return (
<label className="form__group">
<input type="checkbox" checked={this.state.checked} onChange={this.handleClick} className="form__input" />
<span className="form__faux-input"></span>
<span className="form__label">{name}</span>
</label>
)
}
}
function Sidebar({ categories }) {
return (
<div className="sidebar">
<div className="controls">
<div className="filter">
<h2 className="filter__heading">Filter By Category</h2>
<form className="filter-form">
{!categories
? <Spinner />
: categories.map((item) => (
<Checkbox key={item} name={item} />
))
}
<div className="form__group">
<button className="btn btn--rectangle btn--green">
<span className="btn-wrapper">Reset</span>
</button>
</div>
</form>
</div>
</div>
</div>
);
}
class App extends Component {
state = {
books: null,
categories: null
}
async componentDidMount() {
const { books, categories } = await getBooks();
this.setState(() => ({
books: books,
categories: categories
}));
}
render() {
const { books } = this.state;
const { categories } = this.state;
return (
<div className="App">
<Header />
<main className="main">
<div className="uiContainer">
<Sidebar
categories={categories}
/>
{!books
? <Spinner />
: <Card books={books} />
}
</div>
</main>
</div>
);
}
}
I dont 100% understand the question, but if you want to make a section like
[x] cats
[x] dogs
[ ] rabbits // dont show rabbits
Then you can keep the selection and the result part in one react element, if you dont understand the 'lifting state up' section
the state should contain an array of objects like this:
[{
allow: true,
title: 'cat'
},
{allow: false, title: 'rabbit'}]
To update the list use something like this:
this.state.map(({title, allow}) => (
<div>
<TickBox onClick={() => this.toggleAnimal(title)} value={allow}/>
<p>{animalName}</p>
</div>
)
toggleAnimal function should find the animal using the title, and update the state
Then you can filter out all the not allowed animals
this.state
.filter(animal => animal.allowed)
.map(a => <p>{a.title}</p>)
lifting state up
At this point you have 1 component, and the render function looks like this:
<h1>Please select the animals</h1>
{
animals.map(_ => <div><tickbox onClick={() => this.handleToggle(title)} /><title></div>)
}
<h1>Here are the filtered animals</h1>
{
animals.filter(a => a.allow).map(animal => animal.title).map(/* to JSX */)
}
It would be prettier and more responsive if the root component would look like this:
render () {
<SelectAnimals toggle={handleToggle} animals={this.state} />
<ShowFilteredAnimals animals={this.state} />
}
handleToggle (title) {
this.setState(...)
}
As so can see, the SelectAnimals gets a function as an argument, it can communicate with it's parent, by calling props.toggle (with the title as argument)
So SelectAnimals would look like this:
props.animals.map(animal => (
<div>
<TickBox onClick=(() => {props.toggle(animal.title)}) /> // HERE
<p>{animal.title}</p>
</div>
))
So when the tick-box fires a click event, it calls an arrow func. that calls props.toggle function with the title
In the parent of SelectAnimals, the parent element binds a handler function to SelectAnimals.toggle like this:
handleToggle (title) { // the child element called this function, it just got copied
}
PS: I made some renames in my code the handleToggle function can be the same as toggleAnimals
The parent component App needs to be able to tell Card what the selected category is, assuming Card is where the list renders.
To do that, you can:
1) create a callback function inside <App>:
_setCurrentCategory(selection) {
this.setState({currentCategory: selection})
}
2) pass it to <Checkbox /> as a prop and use it in an onChange:
class Checkbox extends Component {
render() {
const {name, setCurrentCategoryCallback } = this.props
return (
<label className="form__group">
<input
type="checkbox"
onChange={() => setCurrentCategoryCallback(name)}
className="form__input"
/>
<span className="form__faux-input"></span>
<span className="form__label">{name}</span>
</label>
)
}
}
.. this will change the state in the parent so that you can then
2) then pass the state from <App /> to <Card />:
<Card
currentCategory={this.state.currentCategory}
books={books}
/>
^^ assuming that this is where the filtered list will render. Inside the Card component, you can filter/order then render the list as you please since it now has both the list of books, and the currently selected category.
This is very loosely coded, but hopefully you get the idea!
also, when deconstructing you don't need to do this:
const { books } = this.state;
const { categories } = this.state;
you can instead do this: const { books, categories} = this.state since they are both coming from state :)

Categories

Resources