I'm new to react and learning hooks. I found examples online that im converting to use hooks as practice but running into an issue with one. The example i'm converting, I can't figure out why the "Remove" button for function handleRemoveSpecificRow on the table row doesn't work. It should remove the the row. I'm stuck at this point. Any help appreciated.
Demo: https://codesandbox.io/s/zq185w2zkm
import React, { useState } from "react";
import ReactDOM from "react-dom";
import { Table, Button, Input } from "reactstrap";
function App() {
const [rows, setRows] = useState([{}]);
const handleChange = index => e => {
const { name, value } = e.target;
const rows = [...rows];
rows[index] = {
[name]: value
};
setRows(rows);
};
const handleAddRow = () => {
const item = {
column_1: "",
column_2: ""
};
setRows([...rows, item]);
};
const handleRemoveRow = () => {
setRows(rows.slice(0, -1));
};
const handleRemoveSpecificRow = index => () => {
const rows = [...rows];
rows.slice(index, 1);
setRows(rows);
};
return (
<div>
<Table>
<thead className="thead-light">
<tr>
<th>#</th>
<th>Column 1</th>
<th>Column 2</th>
<th />
</tr>
</thead>
<tbody>
{rows.map((item, index) => (
<tr id="addr" key={index}>
<td>{index}</td>
<td>
<Input
type="text"
name="name"
value={rows[index].column_1}
onChange={handleChange(index)}
/>
</td>
<td>
<Input
type="text"
name="mobile"
value={rows[index].column_2}
onChange={handleChange(index)}
/>
</td>
<td>
<Button
outline
color="danger"
onClick={handleRemoveSpecificRow(index)}
>
Remove
</Button>
</td>
</tr>
))}
</tbody>
</Table>
<Button onClick={handleAddRow}>Add Row</Button>
<Button color="danger" onClick={handleRemoveRow}>
Delete Row
</Button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
I too had my fair share of doubts like this, focus towards array/object manipulation techniques. Always manipulate data in a controlled way using unique id, key, etc..
https://codesandbox.io/s/y2lqj9qnjz
import React, { useState } from "react";
import ReactDOM from "react-dom";
import { Table, Button, Input } from "reactstrap";
function App() {
const [rows, setRows] = useState([]);
const handleChange = item => e => {
const { name, value } = e.target;
// const rows = [...rows];
// rows[index] = {
// [name]: value
// };
let items = rows.map(row => {
if (row.id === item.id) {
row[name] = value;
}
return row;
});
setRows(items);
};
const handleAddRow = () => {
let item = {
id: rows.length + 1,
column_1: "",
column_2: ""
};
setRows([...rows, item]);
};
const handleRemoveRow = () => {
setRows(rows.slice(0, -1));
};
const handleRemoveSpecificRow = item => () => {
// const rows = [...rows];
// rows.splice(index, 1);
// setRows(rows);
let items = rows.filter(row => row.id != item.id);
setRows(items);
};
console.log(rows);
return (
<div>
{rows.length != 0 && (
<Table>
<thead className="thead-light">
<tr>
<th>#</th>
<th>Column 1</th>
<th>Column 2</th>
<th />
</tr>
</thead>
<tbody>
{rows.map((item, index) => (
<tr id="addr" key={index}>
<td>{item.id}</td>
<td>
<Input
type="text"
name="column_1"
value={item.column_1}
onChange={handleChange(item)}
/>
</td>
<td>
<Input
type="text"
name="column_2"
value={item.column_2}
onChange={handleChange(item)}
/>
</td>
<td>
<Button
outline
color="danger"
onClick={handleRemoveSpecificRow(item)}
>
Remove
</Button>
</td>
</tr>
))}
</tbody>
</Table>
)}
<Button onClick={handleAddRow}>Add Row</Button>
<Button color="danger" onClick={handleRemoveRow}>
Delete Row
</Button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
https://codesandbox.io/s/y2lqj9qnjz
I've updated the link above.
import React, { useState } from "react";
import ReactDOM from "react-dom";
import { Table, Button, Input } from "reactstrap";
function App() {
const [rows, setRows] = useState([
{
id: 1,
column_1: "",
column_2: ""
}
]);
const handleChange = (item) => (e) => {
const { name, value } = e.target;
// const rows = [...rows];
// rows[index] = {
// [name]: value
// };
let items = rows.map((row) => {
if (row.id === item.id) {
row[name] = value;
}
return row;
});
setRows(items);
};
const handleAddRow = () => {
let item = {
id: rows[rows.length - 1].id + 1,
column_1: "",
column_2: ""
};
setRows([...rows, item]);
};
const handleRemoveRow = () => {
setRows(rows.slice(0, -1));
};
const handleRemoveSpecificRow = (item) => () => {
// const rows = [...rows];
// rows.splice(index, 1);
// setRows(rows);
let items = rows.filter((row) => row.id != item.id);
setRows(items);
};
return (
<div>
{rows.length != 0 && (
<Table>
<thead className="thead-light">
<tr>
<th>#</th>
<th>Column 1</th>
<th>Column 2</th>
<th />
</tr>
</thead>
<tbody>
{rows.map((item, index) => (
<tr id="addr" key={index}>
<td>{item.id}</td>
<td>
<Input
type="text"
name="column_1"
value={item.column_1}
onChange={handleChange(item)}
/>
</td>
<td>
<Input
type="text"
name="column_2"
value={item.column_2}
onChange={handleChange(item)}
/>
</td>
<td>
<Button
outline
color="danger"
onClick={handleRemoveSpecificRow(item)}
>
Remove
</Button>
</td>
</tr>
))}
</tbody>
</Table>
)}
<Button onClick={handleAddRow}>Add Row</Button>
<Button color="danger" onClick={handleRemoveRow}>
Delete Row
</Button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Related
I have a series of 4 dropdowns with filtering of the available items of the 2nd based on the selection of the 1st, and so on down to the 4th. The 1st, 2nd and 3rd work as expected, but the 4th never has any items in it. All of the typeahead dropdowns use the same component, so there is no difference in the code used between them.
Here is the component that is giving me grief, the relevant typeahead is the last that uses availableSpots for its items.
import React, { useState, useEffect } from "react";
import Button from "react-bootstrap/Button";
import "bootstrap/dist/css/bootstrap.min.css";
import { Table } from "react-bootstrap";
import { UseFetchCountries, UseFetchStates, UseFetchWaters, UseFetchSpots } from "./hooks/SearchBarHooks";
import TypeAheadDropDown from "./Typeaheads/TypeAheadDropDown";
const SearchBar = () => {
const dbCountries = UseFetchCountries("http://localhost:3033/country");
const dbStates = UseFetchStates("http://localhost:3033/state");
const dbWaters = UseFetchWaters("http://localhost:3033/water");
const dbSpots = UseFetchSpots("http://localhost:3033/spot");
const [availableStates, setAvailableStates] = useState(dbStates);
const [availableWaters, setAvailableWaters] = useState(dbWaters);
const [availableSpots, setAvailableSpots] = useState(dbSpots);
const [selectedCountry, setSelectedCountry] = useState({});
const [selectedState, setSelectedState] = useState({});
const [selectedWater, setSelectedWater] = useState({});
const [selectedSpot, setSelectedSpot] = useState({});
const selectCountryHandler = (country) => {
setSelectedCountry(country);
setAvailableStates(dbStates.filter((state) => state.country_id == country.id));
console.log("===> selectCountryHandler > availableSpots: " + JSON.stringify(availableSpots));
};
const selectStateHandler = (state) => {
setSelectedState(state);
setAvailableWaters(dbWaters.filter((water) => state.id == water.state_id));
console.log("===> selectStateHandler > availableSpots: " + JSON.stringify(availableSpots));
};
const selectWaterHandler = (water) => {
setSelectedWater(water);
console.log("===> water: " + JSON.stringify(water));
console.log("===> dbSpots: " + JSON.stringify(dbSpots));
setAvailableSpots(dbSpots.filter((spot) => spot.water_id == water.id));
console.log("===> Filtered Spots: " + JSON.stringify(dbSpots.filter((spot) => spot.water_id == water.id)));
console.log("===> selectStateHandler > availableSpots: " + JSON.stringify(availableSpots));
};
const selectSpotHandler = (spot) => {
setSelectedSpot(spot);
};
const isButtonEnabled = () => {
if (selectedCountry.id && selectedState.id && selectedWater.id && selectedSpot.id) {
return "{{false}}";
}
return "{{disabled}}";
};
useEffect(() => {
setAvailableSpots(dbSpots.filter((spot) => spot.water_id == selectedWater.id));
}, [dbSpots, selectedWater]);
return (
<div className="container-fluid h-100">
<div className="row bordered-component">
<h4>Select your filters</h4>
<Table className="table search-bar-filter">
<tbody>
<tr>
<td className="dd-label-cell">
<h6>Country: </h6>
</td>
<td>
<TypeAheadDropDown items={dbCountries} countrySelected={selectCountryHandler} />
</td>
</tr>
<tr>
<td className="dd-label-cell">
<h6>State/Region: </h6>
</td>
<td>
<TypeAheadDropDown items={availableStates} parent={selectedCountry} stateSelected={selectStateHandler} />
</td>
</tr>
<tr>
<td className="dd-label-cell">
<h6>Water: </h6>
</td>
<td>
<TypeAheadDropDown items={availableWaters} parent={selectedState} waterSelected={selectWaterHandler} />
</td>
</tr>
<tr>
<td className="dd-label-cell">
<h6>Spot: </h6>
</td>
<td>
<TypeAheadDropDown items={availableSpots} parent={selectedWater} spotSelected={selectSpotHandler} />
</td>
</tr>
<tr>
<td> </td>
<td>
<Button className="float-right" variant="success" disabled={isButtonEnabled()}>
Get Fishing Reports
</Button>
</td>
</tr>
</tbody>
</Table>
</div>
</div>
);
};
export default SearchBar;
The console logs show that the data is retrieved from the REST endpoint in the useFetch* functions and dbSpots is properly set. But availableSpots is never set.
Need to delete the data that is highlighted by the checkbox. When I click on the checkbox, in all checkboxes the done becomes: true, then false and i can't remove the highlights. When the remove function is worked, only the first element is deleted. How can write a remove function.
import React from "react";
import { useState } from "react";
const App = () => {
const [user, setUser] = useState([
{id:1, name:"Peter", surname:"Robinson"},
{id:2, name:"Ann", surname:"Walker"},
{id:3, name:"James", surname:"Allen"},
])
const [check, setCheck] = useState({done: false})
const remove = () => {
if (check.done) {
}
}
return <>
<table className="table table-bordered">
<thead>
<tr>
{Object.keys(user[0]).map((elm,i) => {
return <td key={i}>
{elm.charAt(0).toUpperCase() + elm.slice(1)}
</td>
})}
</tr>
</thead>
<tbody>
{
user.map((elem, ind) => {
return <tr key={ind}>
<td>{elem.id}</td>
<td>{elem.name}</td>
<td>{elem.surname}</td>
<td>
<input type="checkbox" name="" id="" onChange={() => setCheck({done: check.done ? false : true})}/>
</td>
</tr>
})
}
</tbody>
</table>
<button className="btn btn-primary ms-2" onClick={() => remove()}>Delete selected</button>
</>
}
export default App;
Thank you.
You should handle the checked state for each user independently, then delete the ones with the checked flag at true:
import React from 'react';
import { useState } from 'react';
const App = () => {
const [user, setUser] = useState([
{ id: 1, name: "Peter", surname: "Robinson", checked: false },
{ id: 2, name: "Ann", surname: "Walker", checked: false },
{ id: 3, name: "James", surname: "Allen", checked: false }
]);
const toggleCheck = (id) => {
const checkedIdx = user.findIndex((u) => u.id === id);
if (checkedIdx === -1) return;
const updatedUser = [...user];
updatedUser[checkedIdx].checked = !updatedUser[checkedIdx].checked;
setUser(updatedUser);
};
const remove = () => {
setUser([...user].filter((u) => !u.checked));
};
return (
<>
<table className="table table-bordered">
<thead>
<tr>
{Object.keys(user[0]).map((elm, i) => {
return (
<td key={i}>{elm.charAt(0).toUpperCase() + elm.slice(1)}</td>
);
})}
</tr>
</thead>
<tbody>
{user.map((elem, ind) => {
return (
<tr key={elem.id}>
<td>{elem.id}</td>
<td>{elem.name}</td>
<td>{elem.surname}</td>
<td>
<input
type="checkbox"
name=""
id=""
onChange={() => toggleCheck(elem.id)}
value={elem.checked}
/>
</td>
</tr>
);
})}
</tbody>
</table>
<button className="btn btn-primary ms-2" onClick={() => remove()}>
Delete selected
</button>
</>
);
};
export default App;
Here is the code to a working sandbox.
I'm working on a react project where the user needs to fill out a form, and according to the "Number of Credits" input - a table should be rendered with some of its rows possibly hidden. My idea was to to set up an isHidden property in the table objects and switch it according to the user input. Then, render only the rows where isHidden = false.
For some reason it's not working, the table keep being rendered as a whole, regardless to the input. I also tried to console.log the rows object, and the isHidden is not changing. Does anybody have an idea?
Here is my code:
App.js:
import Form from './components/Form'
import { useState } from 'react'
import TableDisplay from './components/TableDisplay'
import Banner from './components/Banner'
const App = () => {
const [showBanner, setShowBanner]=useState(false)
const [FormData, setFormData] = useState([
{
id:1,
name:'',
credits:'',
}
])
const [rows, setRows] = useState([
{
id:1,
description:'',
semester:'',
prefix:'ENG',
number:'368/371',
grade:'',
editing:'',
isHidden:false,
},
{
id:2,
description:'',
semester:'',
prefix:'',
number:'',
grade:'',
editing:'',
isHidden:false,
},
{
id:3,
description:'',
semester:'',
prefix:'',
number:'',
grade:'',
editing:'',
isHidden:false,
},
])
const getFormInfo = (info) => {
const id = Math.floor(Math.random() * 10000) + 1
const newName = {id,...info}
setFormData([newName]);
const num = info.credits;
if (num > 40 && num < 70) {
setRows(rows.map((row) =>
row.id===3 ? {isHidden:!row.isHidden}: row
))
}
else if (num>70) {
setRows(rows.map((row) =>
row.id===3 || rows.id===2 ? {isHidden:!row.isHidden}: row
))}
console.log(rows);
}
return (
<>
<Form onAdd={getFormInfo}
onAdd2={() => setShowBanner(!showBanner)
}
/>
{showBanner ? <Banner FormData={FormData}/> : null}
{showBanner ? <TableDisplay rows={rows}/>: null}
</>
);
}
export default App;
Form.js:
import { useState } from "react"
const Form = ({ onAdd,onAdd2}) => {
const[name, setName] = useState('')
const[credits, setCredits] = useState('')
const onSubmit = (e) => {
e.preventDefault();
onAdd({name,credits})
setName('');
setCredits('');
}
return (
<form onSubmit={onSubmit} >
<div className="mb-3">
<label className="form-label">Student Name:</label>
<input value={name}
onChange={(e) =>
setName(e.target.value)}
type="text"
className="form-control"
id="exampleInputEmail1"
aria-describedby="emailHelp"/>
</div>
<div className="mb-3">
<label className="form-label">Total Transfer Credits:</label>
<input value={credits}
onChange={(e) =>
setCredits(e.target.value)}
type="number"
className="form-control"
id="exampleInputPassword1"/>
</div>
<button onClick={onAdd2}
type="submit" className="btn btn-primary">Submit</button>
</form>
)
}
export default Form
TableDisplay.js:
import TableRows from "./TableRows"
const TableDisplay = ({rows, onDelete}) => {
return (
<div>
<table className="table table-striped">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Description</th>
<th scope="col">Semester</th>
<th scope="col">Prefix</th>
<th scope="col">Number</th>
<th scope="col">Grade</th>
<th scope="col">Editing</th>
</tr>
</thead>
<tbody>
{rows.map((row) => {
return <TableRows
key={row.id}
row={row}
/>
})}
</tbody>
</table>
</div>
)
}
export default TableDisplay
TableRows.js:
import React from 'react'
const TableRows = ({row}) => {
return (
<>
<tr>
<td scope="row">{row.id}</td>
<td>{row.description}</td>
<td>{row.semester}</td>
<td>ENG</td>
<td>368/371</td>
<td>{row.grade}</td>
<td><button type="button" className="btn btn-warning">Edit</button></td>
</tr>
</>
)
}
export default TableRows
Thanks!
I make table contain students names, and checkbox for every row in the table, I want so store the students names that checked in the (students) array. How can I do that?
const [data, setData] = useState([]);
const [students, setStudents] = useState([]);
useEffect(() => {
Axios
.get("http://localhost:3003/studentsnotinsections")
.then(result => setData(result.data));
}, []);
console.log(students)
return(
<div>
<table className="table" >
<thead className="thead-dark">
<tr>
<th scope="col">Name</th>
</tr>
</thead>
<tbody>
{data.map((item, id) => {
return <tr key={id}>
<td>{item.FullName}</td>
<td><input type="checkbox" /></td>
</tr>
})}
</tbody>
</table>
</div>
)}
Did you try simply adding onclick function to your checkbox?
const Compon = () => {
const [data, setData] = useState([]);
const [students, setStudents] = useState([]);
useEffect(() => {
Axios
.get("http://localhost:3003/studentsnotinsections")
.then(result => setData(result.data));
}, []);
const addOrRemove = (name) => {
const newStudents = [...students];
const index = newStudents.indexOf(name);
if (index === -1) {
newStudents.push(name);
} else {
newStudents.splice(index, 1);
}
setStudents(newStudents);
console.log(students)
}
return(
<div>
<table className="table" >
<thead className="thead-dark">
<tr>
<th scope="col">Name</th>
</tr>
</thead>
<tbody>
{data.map((item, id) => {
return <tr key={id}>
<td>{item.FullName}</td>
<td><input type="checkbox" onClick={() => addOrRemove(item.FullName)} /></td>
</tr>
})}
</tbody>
</table>
</div>
)
}
You could use onChange handler, it's more standard for input handling that onClick
import { useCallback, useState } from "react";
export default function App() {
const [data, setData] = useState([
{FullName: 'Alex'},
{FullName: 'Richard'}
]);
const [students, setStudents] = useState([]);
let toggleValue = useCallback((event, id) => {
if (event.target.checked) {
setStudents(value => [...value, id])
} else {
setStudents(value => value.filter(it => it !== id))
}
}, [setStudents])
console.log(students)
return(
<div>
<table className="table" >
<thead className="thead-dark">
<tr>
<th scope="col">Name</th>
</tr>
</thead>
<tbody>
{data.map((item, id) => {
return <tr key={id}>
<td>{item.FullName}</td>
<td><input type="checkbox" onChange={(e) => toggleValue(e, id)} /></td>
</tr>
})}
</tbody>
</table>
</div>
)
}
I am trying to delete a product, but it's doesn't show success. I do not know how to get the id of that product to delete
My button onClick = {handleDelete} is import from component in other folder. I try to create handleDelete function, but I missing something in this case.
This is my code for that section
import React, { useState, useEffect } from "react";
import { Container, Row, Col, Table } from "react-bootstrap";
import Loading from "../../components/Loading";
import Button from "../../components/Button/index"
import firebaseApp from "../../api/config";
const ProductTableList = ({
products,
loading,
fetchProductRequest
}) => {
useEffect(() => {
fetchProductRequest();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const firebaseDb = firebaseApp.database();
const [currentId, setCurrentId] = useState("");
if (loading) {
return (
<Container>
<Row>
<Col>
<Loading />
</Col>
</Row>
</Container>
);
}
const handleDelete = (id) => {
const productId = firebaseDb.ref().push().key;
if (window.confirm("Are you sure to delete this record?")) {
firebaseDb
.ref("products")
.child(`products/${productId}`)
.remove((err) => {
if (err) console.log(err);
else setCurrentId("");
});
}
}
const handleUpdate = (event) => {
//TODO
}
return (
<Table striped bordered hover className="product-table">
<thead>
<tr>
<th>No.</th>
<th className="image">Image</th>
<th>Name</th>
<th>Category</th>
<th>Price</th>
<th>Description</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{!!products && products.length > 0 ? (
products.map((product, index) => {
return (
<tr key={index}>
<td>{index}</td>
<td>{product.image}</td>
<td>{product.name}</td>
<td>{product.category}</td>
<td>{product.price}</td>
<td>{product.description}</td>
<td>
<Button onClick={handleDelete} btnText="Delete" />
<Button onClick={handleUpdate} btnText="Update" />
</td>
</tr>
);
})
) :
(
<tr><td className="center-title">Product list is empty!</td></tr>
)}
</tbody>
</Table>
)
}
export default ProductTableList;
Can anyone help me? How do I delete the product that I have selected
Can anyone explain or support for me why? Thank you so much
I made a example, you need to add your function on button click and use your item id to be removed.
import React, { useState, useEffect } from "react";
import { Table } from "react-bootstrap";
const ProductTableList = () => {
const [currentId, setCurrentId] = useState("");
const [products, setProducts] = useState([{
image: 'image',
name: '01',
category: '01',
price: '01',
description: '01'
},
{
image: 'image',
name: '02',
category: '02',
price: '02',
description: '02'
},
{
image: 'image',
name: '03',
category: '03',
price: '03',
description: '03'
}])
const handleDelete = (id) => {
const removeItem = products.filter((item) => item !== products[id])
setProducts(removeItem)
}
return (
<Table striped bordered hover className="product-table">
<thead>
<tr>
<th>No.</th>
<th className="image">Image</th>
<th>Name</th>
<th>Category</th>
<th>Price</th>
<th>Description</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{!!products && products.length > 0 ? (
products.map((product, index) => {
return (
<tr key={index}>
<td>{index}</td>
<td>{product.image}</td>
<td>{product.name}</td>
<td>{product.category}</td>
<td>{product.price}</td>
<td>{product.description}</td>
<td>
<button onClick={() => handleDelete(index)}>Delete</button>
</td>
</tr>
);
})
) :
(
<tr><td className="center-title">Product list is empty!</td></tr>
)}
</tbody>
</Table>
)
}
export default ProductTableList;
Also, avoid index as element key
{ items.map((item, index) => (<li key={index}>{item}</li>)) }
When a list item was added or removed, and the key kept the same, the React assumed that the DOM element had not changed, and the app could not render.
An alternative to cases that the list doesn't have a unique ID is to generate one using shortID.
https://www.npmjs.com/package/shortid