I am building a contact list to try to learn React. The API data is populating into my table. I want to be able to edit the contact information if needed. My question is how do I get the table row to populate into a form that spawns in a Modal after a button is clicked? There is so much out there that I would like to know the best approach for what I have created.
This is the page that comes up with the table in it:
const [show, setShow] = useState(false);
const handleClose = () = setShow(false);
const handleShow = () = setShow(true);
return (
<>
<Table>
<thead>
<tr>
<td>First Name</td>
<td>Last Name</td>
<td>Email </td>
<td>Phone</td>
</tr>
</thead>
<tbody>
{data.map((results, index) => (
<tr key={index}>
<td>{results.first_name}</td>
<td>{results.last_name}</td>
<td>{results.email}</td>
<td>{results.phone_number}</td>
<td>
<Button variant="warning" onClick={handleShow}>Edit</Button>
</td>
</tr>
))}
</tbody>
</Table>
<Modal className="edit" show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Contact</Modal.Title>
</Modal.Header>
<Modal.Body>
{<Contact_Form />}
</Modal.Body>
<Modal.Footer>
<Button variant="danger" onClick={}>Delete</Button>
<Button variant="primary" onClicke={}>Update</Button>
</Modal.Footer>
</Modal>
</>
);
And this is the form that spawns in the modal when clicked. This is in a different file than the one above.
function Contact_Form(){
return (
<Form>
<Row className="mb-3">
<Form.Group as={Col}>
<Form.Label>First Name</Form.Label>
<Form.Control placeholder="" />
</Form.Group>
<Form.Group as={Col}>
<Form.Label>Last Name</Form.Label>
<Form.Control placeholder="" />
</Form.Group>
</Row>
<Form.Group className="mb-3">
<Form.Label>Email</Form.Label>
<Form.Control placeholder="someone#example.com" />
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Phone Number</Form.Label>
<Form.Control placeholder="(XXX) XXX-XXXX" />
</Form.Group>
</Form>
);
}
export default Contact_Form;
What am I missing? Does my contact_form need to be in the same file?
I have looked through multiple solutions, but none seem to work with what I have created. I would like a nudge in the right direction.
You could use another state that holds the selected result data, and pass it as a prop to the Contact_Form:
const [selected, setSelected] = useState(null);
const handleShow = (results) => {
setSelected(results);
setShow(true);
};
return (
...
<tbody>
{data.map((results, index) => (
<tr key={index}>
<td>{results.first_name}</td>
<td>{results.last_name}</td>
<td>{results.email}</td>
<td>{results.phone_number}</td>
<td>
<Button variant="warning" onClick={() => handleShow(results)}>Edit</Button>
</td>
</tr>
))}
</tbody>
...
<Modal className="edit" show={show} onHide={handleClose}>
...
<Modal.Body>
{<Contact_Form data={selected} />}
</Modal.Body>
...
</Modal>
)
Related
I'm developing an app that can track pokemon stocks in the store. I fetch the name of data from pokeAPI. Each pokemon is supposed to have its own stock (my pokemon stock is still static) that can be updated in the next page when i click the pokemon's name.
This is the second page:
All in this page is still static. the only dinamic is the pokemons name that I get from params.
How can I update stock of each pokemon and save it? and show it in certain page? Using params (but i think it's not the best practice)? I read I can save the stock to localStorage and accessit later but where should I start from my code?
This is the modal that i will use to update my stock and pass the value to the second page.
first page:
export default function Homepage() {
const [pokemons, setPokemons] = useState([])
const [query, setQuery] = useState("")
const [search, setSearch] = useState("")
let navigate = useNavigate();
const getPokemons = async () => {
try {
let response = await axios.get(`${baseUrl}`)
let pokemons = await response.data.results
setPokemons(pokemons)
} catch (err) {
console.log(err.message)
}
}
const searchPokemon = async (pokemon, e) => {
console.log("masuk seacrh")
try {
if (e.key === 'Enter') {
let response = await axios.get(`${baseUrl} ${pokemon}`)
let data = await response.json()
setSearch(search)
}
} catch (err) {
console.log(err.message)
}
}
useEffect(() => {
getPokemons()
}, [])
// useEffect(() => {
// searchPokemon()
// }, [])
return (
<div className="app-container">
<h1 className="title">Stok Pokémon</h1>
<div className="">
<img src={searchIcon} className="search-icon" />
<input type="text"
className="search-box"
placeholder="Cari Pokémon"
onChange={(e) => setQuery(e.target.value)}
onKeyUp={searchPokemon} />
</div>
<div className="row">
<div className="col">
<Table className="table d-flex row">
<thead>
<tr className="d-flex justify-content-between th-border">
<th scope="col">Nama</th>
<th scope="col" className="d-flex text-right">Stok</th>
</tr>
</thead>
<tbody>
{
pokemons.filter(pokemon => pokemon.name.toLowerCase().includes(query)).map((pokemon, i) => {
console.log(pokemon)
return (
<tr className="d-flex justify-content-between">
<td key={i + 1} className="table-link" onClick={() => {
navigate(`/pokemon/${pokemon.name}`)
}} style={{ textTransform: 'capitalize' }}>{pokemon.name}</td>
<td className="pokemon-stock">10 pcs</td>
</tr>
)
})
}
</tbody>
</Table>
</div>
</div>
</div >
)
}
second page:
function UpdateStockModal(props) {
const [input, setInput] = useState({
pcs: 0,
lusin: 0
})
let navigate = useNavigate();
const [pcs, setPcs] = useState("")
const handleChange = e => {
let newValue = {
...input,
[e.target.name]: e.target.value,
};
setInput(newValue);
};
return (
<Modal
{...props}
size="lg"
aria-labelledby="contained-modal-title-vcenter"
className="modal"
centered
>
<Modal.Header closeButton>
<Modal.Title className="modal-title">
Update stock
</Modal.Title>
</Modal.Header>
<Modal.Body>
<h4>Masukkan jumlah stok yang tersedia di rak saat ini.</h4>
<Container>
<Row>
<Col>Kemasan</Col>
<Col>Jumlah</Col>
<Col>Stok</Col>
</Row>
<Row className="modal-table-body">
<Col >Pcs</Col>
<Col className="d-flex align-items-center">1 x <Form.Control className="modal-input pcs" type="text" name="pcs" value={input.pcs} onChange={handleChange} /> = </Col>
<Col>{input.pcs}</Col>
</Row>
<Row className="modal-table-body">
<Col>Lusin</Col>
<Col className="d-flex align-items-center">12 x <Form.Control name="lusin" className="modal-input lusin" type="text" value={input.lusin} onChange={handleChange} /> = </Col>
<Col>{12 * input.lusin}</Col>
</Row>
<Row className="modal-table-body">
<Col>Total Stok <span>(dalam pcs)</span></Col>
<Col>Lusin</Col>
<Col>{parseInt(12 * input.lusin) + parseInt(input.pcs)}</Col>
</Row>
</Container>
</Modal.Body>
<Modal.Footer>
<Button
variant="primary"
onClick={() => {
navigate(`/update-stock/`)
}} >
Simpan
</Button>
<Button
variant="secondary"
onClick={props.onHide}>
Batal
</Button>
</Modal.Footer>
</Modal>
);
}
export default function PokemonDetail() {
let navigate = useNavigate();
let { name } = useParams()
const [modalShow, setModalShow] = React.useState(false);
return (
<div className="pokemon-detail-page">
<div className="pokemon-detail_button-group">
<Button variant="outline-light" className="prev-button" onClick={() => {
navigate('/')
}}><img src={prevPageIcon}></img>Stok Pokémon</Button>
<Button className="update-stock-button" onClick={() => setModalShow(true)}>Update Stok</Button>
</div>
<p className="pokemon-detail-title" style={{ textTransform: 'capitalize' }}>{name}</p>
<div className="pokemon-detail-subtitle">
<p className="pokemon-detail-sub1">Sisa Stok</p>
<p className="pokemon-detail-sub2">10 pcs</p>
</div>
<div className="pokemon-detail-history">
<p className="pokemon-detail-history1">Riwayat Stok</p>
<p className="pokemon-detail-history2">Satuan stok dalam pcs</p>
</div>
<div>
<Table className='col-xs-12 mt-4' responsive>
<thead>
<tr className="th-border ">
<th scope="col">Waktu</th>
<th scope="col">Kegiatan</th>
<th scope="col">Catatan</th>
<th scope="col">Jumlah</th>
<th scope="col">Stok</th>
</tr>
</thead>
<tbody>
<tr className="align-items-center">
<td className="">2 Apr 2021, 08:00</td>
<td className="table-link">Update Stok</td>
<td className="">"Stok Awal"</td>
<td className="table-count-stock">+10</td>
<td className="table-bold">10</td>
</tr>
</tbody>
</Table>
</div>
<UpdateStockModal
show={modalShow}
onHide={() => setModalShow(false)}
/>
</div>
)
}
Sounds like a shopping cart. I'm assuming you'd like to update the stock for each pokemon. Then on another page, check all the updates before sending an update to the backend. For this I recommend redux saga or rtk query. The state will be globally accessible via the store so you can create your shopping cart securely without using local storage. You can google "redux saga shopping cart" for more examples but these are a couple I've found.
Redux saga
https://github.com/franklsm1/redux-saga-shopping-cart
RTK query
https://codesandbox.io/s/xed9r?file=/README.md
I'm quite new to React and can't seem to figure out how to resolve this issue. Maybe a stupid question but here it goes.
I'm trying to make a system where each Person can add skills and a rating of those skills. Each array consist of a skill element as a string and a star element as an integer.
I've made some dynamic inputfields with an add and a remove function for more/less inputfields.
For each inputfield I have an onchange function handleChangeInput and this works fine with skills as this is has a property of "name".
Now when trying to fetch the value from ReactStars I get the error: Uncaught TypeError: Cannot read properties of undefined (reading 'name'). This error is called from the handleChangeInput function. ReactStars doesn't have any property of type either.
How will I be able to fetch the value from ReactStars, if it's even possible? I want to set the useState of inputfields with the values of skill and stars of course.
Image of the TypeError.
The component I use is: react-rating-stars-component
https://www.npmjs.com/package/react-rating-stars-component
Hope this explains the problem. Otherwise let me know. Thanks.
const [data, setData] = useState({});
const [inputField, setInputField] = useState([{ skill: "", stars: 0 }]);
const handleChangeInput = (index, event) => {
const values = [...inputField];
values[index][event.target.name] = event.target.value;
setInputField(values);
setData(values);
};
const handleAddFields = () => {
setInputField([...inputField, { skill: "", stars: 0 }]);
};
const handleRemoveFields = (index) => {
const values = [...inputField];
console.log(inputField.length);
values.splice(index, 1);
setInputField(values);
setData(values);
};
return(
<div>
{inputField?.map((inputField, index) => (
<div key={index}>
<Row>
<Col xs={5} md={5}>
<Form.Group as={Col}>
<Form.Control
className="mb-3"
type="text"
id="skill"
name="skill"
value={inputField?.skill}
onChange={(event) => handleChangeInput(index, event)}
/>
</Form.Group>
</Col>
<Col xs={4} md={4}>
<Form.Group>
<ReactStars
type="number"
name="stars"
count={5}
size={24}
id="stars"
onChange={(event) => handleChangeInput(index, event)}
color="rgba(230, 230, 230, 255)"
emptyIcon={<i className="fa-solid fa-star"></i>}
filledIcon={<i className="fa-solid fa-star"></i>}
value={inputField.stars}
/>
</Form.Group>
</Col>
<Col xs={3} md={3}>
<div className="btn-section">
<button
type="button"
className="round-btn"
onClick={() => handleAddFields()}
>
<i className="fa-solid fa-plus"></i>
</button>
<button
type="button"
className="round-btn"
onClick={() => handleRemoveFields(index)}
>
<i className="fa-solid fa-minus"></i>
</button>
</div>
</Col>
</Row>
</div>
))}
</div>
);
The package passes the new value, not an event containing it.
You can instead simply bind the change handler differently in each case such that the context you need is passed through:
const [data, setData] = useState({});
const [inputField, setInputField] = useState([{ skill: "", stars: 0 }]);
const handleChangeInput = (index, name, value) => {
const values = [...inputField];
values[index][name] = value;
setInputField(values);
setData(values);
};
const handleAddFields = () => {
setInputField([...inputField, { skill: "", stars: 0 }]);
};
const handleRemoveFields = (index) => {
const values = [...inputField];
console.log(inputField.length);
values.splice(index, 1);
setInputField(values);
setData(values);
};
return(
<div>
{inputField?.map((inputField, index) => (
<div key={index}>
<Row>
<Col xs={5} md={5}>
<Form.Group as={Col}>
<Form.Control
className="mb-3"
type="text"
id="skill"
name="skill"
value={inputField?.skill}
onChange={(event) => handleChangeInput(index, 'skill', event.target.value)}
/>
</Form.Group>
</Col>
<Col xs={4} md={4}>
<Form.Group>
<ReactStars
type="number"
name="stars"
count={5}
size={24}
id="stars"
onChange={(newValue) => handleChangeInput(index, 'stars', newValue)}
color="rgba(230, 230, 230, 255)"
emptyIcon={<i className="fa-solid fa-star"></i>}
filledIcon={<i className="fa-solid fa-star"></i>}
value={inputField.stars}
/>
</Form.Group>
</Col>
<Col xs={3} md={3}>
<div className="btn-section">
<button
type="button"
className="round-btn"
onClick={() => handleAddFields()}
>
<i className="fa-solid fa-plus"></i>
</button>
<button
type="button"
className="round-btn"
onClick={() => handleRemoveFields(index)}
>
<i className="fa-solid fa-minus"></i>
</button>
</div>
</Col>
</Row>
</div>
))}
</div>
);
I am using formik and fieldarray for creating dynamic form and I am using react-select. I am working on edit of items. On Edit if there exists previous values then those values must be shown and those values can be changed, but somehow I am unable to create. Here's my code.
here is the code for initial value of formik
const initialOrderItemData = orderItems && orderItems.map((orderItem) => {
return {
itemId: orderItem.itemId,
quantity: orderItem.quantity,
}
});
const initialOrderInfo = {
orderedItemDataArray: initialOrderItemData,
};
This is my form
<Formik
initialValues={initialOrderInfo}
onSubmit={handleSubmit}
validationSchema={HotelOrderSchema}
>
{({ errors, setFieldValue, touched, values, name, }) => (
<Form>
<Row>
<Table size="sm" hover>
<thead>
<tr>
<th>Item</th>
<th>Quantity</th>
<th>Operation</th>
</tr>
</thead>
<tbody>
<FieldArray
name="orderedItemDataArray"
render={({ remove, push }) => (
<>
{values.orderedItemDataArray.length > 0 &&
values.orderedItemDataArray.map(
(order, index) => (
<tr key={index}>
<td>
<Select
as="select"
name={`orderedItemDataArray.${index}.itemId`}
options={menuOptions}
placeholder="Type"
// value={selectedItems(order.itemId)}
value={menuOptions.find(op => {
return op.value === order.itemId
})}
/>
</td>
<td>
<Field
type="number"
className="form-control"
name={`orderedItemDataArray.${index}.quantity`}
min="0"
placeholder="Quantity"
/>
</td>
<td>
<ButtonGroup>
{values.orderedItemDataArray
.length !== 1 && (
<Button
color="primary"
size="sm"
onClick={() => remove(index)}
>
-
</Button>
)}
{values.orderedItemDataArray
.length -
1 ===
index && (
<Button
color="secondary"
size="sm"
onClick={() =>
push({
itemId: '',
quantity: '',
})
}
>
+
</Button>
)}
</ButtonGroup>
</td>
</tr>
)
)}
</>
)}
/>
</tbody>
</Table>
</Row>
</Form>
</Formik>
with this code I am able to get the value but I am unable to change the value and set the new value.
In your select component you must include an onChange event like so
//set a state for the changed selected option
const [selectedOption,setSelectedOption] = useState()
<Select
as="select"
name={`orderedItemDataArray.${index}.itemId`}
options={menuOptions}
placeholder="Type"
// value={selectedItems(order.itemId)}
value={menuOptions.find(op => {
return op.value === order.itemId
onChange={(e) => {
setSelectedOption(e.target.value)
}}
})}
/>
//then if you wanna access the selected option you can use the useState value.
<p>{selectedOption}</p>
I have a modal component and form component. I am trying to pass the data using React hooks from the form back to the modal. I am having trouble doing this. Here is what I have so far -
Modal(Parent)
interface ISystem {
systemName: string;
allowNumber: number;
statusCode: string;
createdBy?: string;
createdDate?: string;
}
const ModalForm = (props) => {
const { buttonLabel, className } = props;
const [modal, setModal] = useState(false);
const toggle = () => setModal(!modal);
const addButtonHandler = () => {
toggle();
console.log(FORM DATA SHOULD BE HERE)
};
return (
<div>
<Button color="primary" onClick={toggle}>
{buttonLabel}
</Button>
<Modal
isOpen={modal}
toggle={toggle}
className={className}
centered
size="lg"
>
<ModalHeader toggle={toggle}>{buttonLabel}</ModalHeader>
<ModalBody>
<AddSystem></AddSystem>
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={addButtonHandler}>
Add
</Button>{" "}
<Button color="secondary" onClick={toggle}>
Cancel
</Button>
</ModalFooter>
</Modal>
</div>
);
};
export default ModalForm;
This is my Form component
const AddSystem = (props) => {
const [systemId, setSystemId] = useState("");
const [systemName, setSystemName] = useState("");
const [allowNumber, setAllowNumber] = useState("");
const [statusCode, setStatusCode] = useState("");
const [lastModifiedBy, setLastModifiedBy] = useState("");
const submitHandler = (event) => {
event.preventDefault();
};
return (
<Fragment>
<Form onSubmit={submitHandler} className="mx-auto">
<Form.Group as={Row} controlId="systemId">
<Form.Label column sm="2">
{" "}
ID{" "}
</Form.Label>
<Col sm="10">
<Form.Control
type="text"
name="systemId"
placeholder="Leave blank for new system"
value={systemId}
disabled
onChange={(event) => setSystemId(event.target.value)}
/>
</Col>
</Form.Group>
<Form.Group as={Row} controlId="systemName">
<Form.Label column sm="2">
{" "}
Systen Name{" "}
</Form.Label>
<Col sm="10">
<Form.Control
type="text"
name="systemName"
placeholder=""
value={systemName}
onChange={(event) => setSystemName(event.target.value)}
/>
</Col>
</Form.Group>
<Form.Group as={Row} controlId="allowNumber">
<Form.Label column sm="2">
{" "}
Allow Number{" "}
</Form.Label>
<Col sm="10">
<Form.Control
as="select"
name="allowNumber"
value={allowNumber}
onSelect={(event) => setAllowNumber(event.target.value)}
>
<option>Choose...</option>
{["1", "2", "3", "4", "5"].map((opt) => (
<option>{opt}</option>
))}
</Form.Control>
</Col>
</Form.Group>
<Form.Group as={Row} controlId="statusCode">
<Form.Label column sm="2">
{" "}
Status Code{" "}
</Form.Label>
<Col sm="10">
<Form.Control
as="select"
name="statusCode"
value={statusCode}
onSelect={(event) => setStatusCode(event.target.value)}
>
<option>Choose...</option>
{["Active", "Draft"].map((opt) => (
<option>{opt}</option>
))}
</Form.Control>
</Col>
</Form.Group>
<Form.Group as={Row} controlId="lastModifiedBy">
<Form.Label column sm="2">
{" "}
Last Modified By{" "}
</Form.Label>
<Col sm="10">
<Form.Control
type="text"
name="lastModifiedBy"
placeholder=""
value={lastModifiedBy}
disabled
onChange={(event) => setLastModifiedBy(event.target.value)}
/>
</Col>
</Form.Group>
</Form>
</Fragment>
);
};
export default AddSystem;
I don't want to have the button inside the form. The button needs to stay in the modal footer but somehow receive information from the form...This is so that the modal can become re-usable and have any type of form passed into it perhaps
I am using a wizard in my project. In step 2 I have one form with a data table. The purpose of a data table is when I click on the button a form value pushes into a data table. I have done this when I submit the form, a form value append or push into a data table but I am facing an issue is that I can't maintain or set the state of that a data table array.
My code is shown below
import React, {useState, Fragment} from 'react';
import PropTypes from 'prop-types';
import { Field, FieldArray, reduxForm } from 'redux-form'
import FormInput from './../FormInput'
import ConsigneeTables from './ConsigneeTables'
import {
Button,
Card,
CardBody,
Col,
FormGroup,
Input,
Label,
Row,
Table,
Progress
} from 'reactstrap'
const ConsigneeLocations = (props) => {
const {handleSubmit, previousPage} = props
const consigneeData = []
const initialFormState = {
id: null,
consignee_branch_number: '',
consignee_add1: '',
}
const [CurrentConsignee, setCurrentConsignee] = useState(initialFormState)
const [consignees, setConsignees] = useState(consigneeData)
const [consignee, setConsignee] = useState(initialFormState)
const handleInputChange = (event) => {
const {name, value} = event.target
setConsignee({
...consignee,
[name]: value
})
}
const addConsignee = () => {
consignee.id = consignees.length + 1
console.log(consignee);
consignees.push(consignee)
setConsignees([...consignees])
}
return (<form onSubmit={handleSubmit}>
<h2>View users</h2>
<ConsigneeTables consignees={consignees}/>
<Col xs="12" sm="12">
<Card className="card-border">
<CardBody>
<Col xs="12" sm="12">
<Card className="card-border">
<CardBody>
<FormGroup row>
<Col xs="12" lg="6">
<Field name="consignee_branch_number" type="text" component={FormInput} value={consignee.consignee_branch_number} onChange={handleInputChange} label="Branch Sr No" inputPlaceHolder="Enter Branch Sr No"/>
</Col>
<Col xs="12" lg="6">
<Field name="consignee_add1" type="textarea" component={FormInput} value={consignee.consignee_add1} onChange={handleInputChange} label="Address 1" inputPlaceHolder="Enter Address 1"/>
</Col>
</FormGroup>
</CardBody>
<div style={{
paddingBottom: 30
}}>
<Button color="primary" className="btn-pill pull-right" type="button" onClick={addConsignee} style={{
marginRight: '20px'
}}>
Add
</Button>
</div>
</Card>
</Col>
<div style={{
paddingBottom: 30
}}>
<Button color="primary" className="btn-pill pull-left" onClick={previousPage} style={{
marginLeft: '20px'
}}>
<i className="fa fa-chevron-left"/>
Previous
</Button>
<Button color="primary" className="btn-pill pull-right" type="submit" style={{
marginRight: '20px'
}}>
Next
<i className="fa fa-chevron-right"/>
</Button>
</div>
</CardBody>
</Card>
</Col>
</form>);
};
ConsigneeLocations.propTypes = {
handleSubmit: PropTypes.func,
pristine: PropTypes.bool,
previousPage: PropTypes.func,
submitting: PropTypes.bool
};
export default reduxForm({form: 'consigneesLocation', destroyOnUnmount: false, forceUnregisterOnUnmount: true})(ConsigneeLocations);
My data table component
import React from 'react'
const ConsigneeTables = props => (
<table>
<thead>
<tr>
<th>Branch Number</th>
<th>Address</th>
</tr>
</thead>
<tbody>
{props.consignees.length > 0 ? (
props.consignees.map(consignee => (
<tr key={consignee.id}>
<td>{consignee.consignee_branch_number}</td>
<td>{consignee.consignee_add1}</td>
</tr>
))
) : (
<tr>
<td colSpan={3}>No Data</td>
</tr>
)}
</tbody>
</table>
)
export default ConsigneeTables
Please suggest to me how to handle consignees state. I already use setState onClick but I don't know how to handle consignees state every step.