I am new to react. I find this problem when I run the react program. Its say "props.func is not a function"
My AddMembers.js
import React, { useState } from "react";
import { useNavigate } from "react-router-dom
import "../src/css/AddMember.css";
import '../src/data/data.json';
Part of the code of AddMember.js
const AddMember = (props) => {
let movies = [];
const navigate = useNavigate();
const [nameofmovie, setnameofmovie] = useSt
const [moviepic,setmoviepic] = useState("")
const [leadActor,setleadActor] = useState("
const [rating,setRating] = useState("");
function onChangeName(event) {
setnameofmovie(event.target.value);
}
function onChangeImage(event){
setmoviepic(event.target.value);
}
function onChangeActor(event){
setleadActor(event.target.value.split(",")
}
function onChangeRating(event){
setRating(event.target.value);
}
const transferValue = (event) => {
console.log('props obj:', props)
event.preventDefault();
const val = {
"name" :nameofmovie,
"picture of image": moviepic,
"lead actor": leadActor,
"rating": rating
};
props.func(val);
clearState();
navigate("/members");
};
const clearState = () => {
setnameofmovie(' ');
setmoviepic(' ');
setleadActor(' ')
setRating(' ');
};
return (
<div>
<div id="topFormLayerOne" >
<form>
<div id="secondFormLayerTwo">
<label id="labelFour">Movie Names
<input
onChange={onChangeName}
id="inputFour"
type="text"
maxLength="100"
placeholder="Movies Names"
name="moviesName"
></input>
<label id="labelFive">Picture of
<input
onChange={onChangeImage}
id="inputFive"
type="file"
maxLength="100"
placeholder="Name of Images"
name="imageName"
></input>
<label id="labelSix">Lead Actor N
<input
onChange={onChangeActor}
id="inputSix"
type="text"
maxLength="500"
placeholder="Name of Actor"
name="actorName"
></input>
<label id="labelSeven">Rating</la
<input
onChange={onChangeRating}
id="inputSeven"
type="text"
maxLength="10"
placeholder="Rating"
name="movieRating"
></input>
<button onClick={transferValue} i
<button id="removeButton">Remove
</div>
</form>
</div>
<p>{movies}</p>
</div>
);
};
export default AddMember;
My Members.js.
import React, { useState } from "react";
import jsonData from "../src/data/data.json";
import AddMember from "./AddMember";
function Members() {
const [studentData, setStudentData] = useState(jsonData);
const tableRows = studentData.map((info) => {
return (
<tr key={info.id}>
<td>{info.id}</td>
<td>{info.name}</td>
<td><img src={info["picture of image"]} alt=""></img></td>
<td>
{info["lead actor"]}
</td>
<td>{info. Rating}</td>
</tr>
);
})
const addRows = (data) => {
const totalStudents = studentData.length;
data.id = totalStudents + 1;
const updatedStudentData = [...studentData];
updatedStudentData.push(data);
setStudentData(updatedStudentData);
};
return (
<div>
<table className="table table-stripped">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Image</th>
<th>Actors</th>
<th>Rating</th>
</tr>
</thead>
<tbody>{tableRows}</tbody>
</table>
<AddMember func={()=>addRows()} />
</div>
);
}
export default Members;
This is the json data file
[{ "id": 1,"name" : "Ticket to Paradise","picture of image":"../images/Ticket_to_Paradise.jpg", "lead actor": ["George Clooney, Julia Roberts"],"rating":5}]
If I don't add the navigate(), everything is working fine. But once I add the navigate, the error "Uncaught TypeError: props.func is not a function" occurs. The image also cannot work
How i am going to define image in json?
THe AddMember.js
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import "../src/css/AddMember.css";
import '../src/data/data.json';
const AddMember = (props) => {
let movies = [];
const navigate = useNavigate();
const [nameofmovie, setnameofmovie] = useState("");
const [moviepic,setmoviepic] = useState("");
const [leadActor,setleadActor] = useState("");
const [rating,setRating] = useState("");
function onChangeName(event) {
setnameofmovie(event.target.value);
}
function onChangeImage(event){
setmoviepic(event.target.value);
}
function onChangeActor(event){
setleadActor(event.target.value.split(","))
}
function onChangeRating(event){
setRating(event.target.value);
}
const transferValue = (event) => {
console.log('props obj:', props)
event.preventDefault();
const val = {
"name" :nameofmovie,
"picture of image": moviepic,
"lead actor": leadActor,
"rating":rating
};
props.func(val);
clearState();
navigate("/members");
};
const clearState = () => {
setnameofmovie(' ');
setmoviepic(' ');
setleadActor(' ')
setRating(' ');
};
return (
<div>
<div id="topFormLayerOne" >
<form>
<div id="secondFormLayerTwo">
<label id="labelFour">Movie Names</label>
<input
onChange={onChangeName}
id="inputFour"
type="text"
maxLength="100"
placeholder="Movies Names"
name="moviesName"
></input>
<label id="labelFive">Picture of Movies</label>
<input
onChange={onChangeImage}
id="inputFive"
type="file"
maxLength="100"
placeholder="Name of Images"
name="imageName"
></input>
<label id="labelSix">Lead Actor Names</label>
<input
onChange={onChangeActor}
id="inputSix"
type="text"
maxLength="500"
placeholder="Name of Actor"
name="actorName"
></input>
<label id="labelSeven">Rating</label>
<input
onChange={onChangeRating}
id="inputSeven"
type="text"
maxLength="10"
placeholder="Rating"
name="movieRating"
></input>
<button onClick={transferValue} id="submitButton">Submit</butto
<button id="removeButton">Remove Movie</button>
</div>
</form>
</div>
<p>{movies}</p>
</div>
);
};
export default AddMember;
the Member.js
import React, { useState } from "react";
import jsonData from "../src/data/data.json";
import AddMember from "./AddMember";
function Members() {
const [studentData, setStudentData] = useState(jsonData);
const tableRows = studentData.map((info) => {
return (
<tr key={info.id}>
<td>{info.id}</td>
<td>{info.name}</td>
<td><img src={info["picture of image"]} alt=""></img></td>
<td>
{info["lead actor"]}
</td>
<td>{info.rating}</td>
</tr>
);
})
const addRows = (data) => {
const totalStudents = studentData.length;
data.id = totalStudents + 1;
const updatedStudentData = [...studentData];
updatedStudentData.push(data);
setStudentData(updatedStudentData);
};
return (
<div>
<table className="table table-stripped">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Image</th>
<th>Actors</th>
<th>Rating</th>
</tr>
</thead>
<tbody>{tableRows}</tbody>
</table>
<AddMember func={()=>addRows()} />
</div>
);
}
export default Members;
Here I rewrite the Member.js and AddMember.js.
Related
I am new to react. I have difficulty deleting the table row. I find that the data variable in the member.js does not show the date that I input that I submitted. Why is that so? Can anyone find a solution why I can't delete the rows that I added.
My App.css
import dataMovielist from '../src/data/data.json';
import Members from './Members';
import AddMember from './AddMember';
import {useState} from 'react';
function App() {
const [datalist, setdatalist] = useState(dataMovielist);
const onSubmitHandler = ((newHobbies)=>{
setdatalist((oldHobbies)=>[newHobbies,...oldHobbies])
})
return (
<div className="App">
<AddMember onSubmitHandler = {onSubmitHandler} />
<Members data={datalist} onSubmitHandler = {onSubmitHandler}/>
</div>
);
}
export default App;
My Member.js
import React, { useState } from "react";
import '../src/css/movie.css'
import moviesdata from '../src/data/data.json';
function Members({ data, onSubmitHandler }) {
const [moviesData, setMoviesData] = useState(data);
console.log(moviesData)
const deleteMoviesHandler = (personIndex) => {
alert("hi")
console.log(moviesData)
moviesData.splice(personIndex, 1);
setMoviesData([...moviesData]);
};
const tableRows = (data) =>
data.map((info, num) => {
return (
<tr key={info.id} >
<td>{info.id}</td>
<td>{info.name}</td>
<td>
<img src={info["picture of image"]} alt=""></img>
</td>
<td>{info["lead actor"]}</td>
<td>{info.rating}</td>
<td><button onClick={()=>deleteMoviesHandler(num)}>Delete</button></td>
</tr>
);
});
return (
<div>
<table className="table table-stripped">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Image</th>
<th>Actors</th>
<th>Rating</th>
</tr>
</thead>
<tbody>{tableRows(data)}</tbody>
</table>
</div>
);
}
export default Members;
My addMember.js
import React, { useState } from "react";
import "../src/css/addMember.css";
const AddMember = ({onSubmitHandler}) => {
const [idmovie,setidmovie] = useState("");
const [nameofmovie, setnameofmovie] = useState("");
const [moviepic, setmoviepic] = useState("");
const [leadActor, setleadActor] = useState("");
const [rating, setRating] = useState("");
function onChangeId(event){
setidmovie(event.target.value);
}
function onChangeName(event) {
setnameofmovie(event.target.value);
}
function onChangeImage(event) {
setmoviepic(event.target.value);
}
function onChangeActor(event) {
setleadActor(event.target.value.split(","));
}
function onChangeRating(event) {
setRating(event.target.value);
}
const transferValue = (event) => {
event.preventDefault();
const val = {
id : idmovie,
name: nameofmovie,
"picture of image": moviepic,
"lead actor": leadActor,
rating: rating,
};
onSubmitHandler(val);
clearState();
};
const clearState = () => {
setidmovie("");
setnameofmovie("");
setmoviepic("");
setleadActor("");
setRating("");
};
return (
<div>
<div id="topFormLayerOne">
<form>
<div id="secondFormLayerTwo">
<label id="labelTwo">Please key in the Id</label>
<input
id="inputThree"
type="text"
maxLength="10"
onChange={onChangeId}
placeholder="Please key in the Id"
></input>
<label id="labelFour">Movie Names</label>
<input
onChange={onChangeName}
id="inputFour"
type="text"
maxLength="100"
placeholder="Movies Names"
name="moviesName"
></input>
<label id="labelFive">Picture of Movies</label>
<input
onChange={onChangeImage}
id="inputFive"
type="file"
maxLength="100"
placeholder="Name of Images"
name="imageName"
></input>
<label id="labelSix">Lead Actor Names</label>
<input
onChange={onChangeActor}
id="inputSix"
type="text"
maxLength="500"
placeholder="Name of Actor"
name="actorName"
></input>
<label id="labelSeven">Rating</label>
<input
onChange={onChangeRating}
id="inputSeven"
type="text"
maxLength="10"
placeholder="Rating"
name="movieRating"
></input>
<button onClick={transferValue} id="submitButton">
Submit
</button>
<button id="removeButton">Remove Movie</button>
</div>
</form>
</div>
</div>
);
};
export default AddMember;
My data.json
[{ "id": 1,"name" : "Ticket to Paradise","picture of image":"../images/Ticket_to_Paradise.jpg", "lead actor": ["George Clooney", "Julia Roberts"],"rating":5}]
Can you help me solve theis problems?
Problem
Your deleteMoviesHandler deletes items from their local state of Members and what is rendered is the parent state.
Solution
You don't need to keep another state inside the Members component.
Move the deleteMoviesHandler to the parent component and pass it down to Members.
function App() {
...
...
const deleteMoviesHandler = (personIndex) => {
datalist.splice(personIndex, 1);
setdatalist([...datalist]);
};
...
...
<Members
...
...
deleteMoviesHandler={deleteMoviesHandler}
/>
Use it from Members component.
function Members({ data, onSubmitHandler, deleteMoviesHandler }) {...}
Working Demo:
I'm trying to implement a delete operation on table rows. but it keeps on throwing errors. So I need some help to figure this out.
I don't know to how to set id that can be auto incremented so I gave Date.now().
now what I want is to delete the row that i perform the delete operation on.
I'm new to react so sorry for the bad code. thank you in advance.
heres my code
import React from "react";
import { CirclesWithBar } from "react-loader-spinner";
import { useState } from "react";
import Table from "./Table";
function Main() {
// *****INITIALIZING*****
const [tableData, setTableData] = useState([])
const [formInputData, setformInputData] = useState(
{
id: Date.now(),
Name: '',
email: '',
}
);
const [loading, setloading] = useState(false);
// const deleteTableRows = (index)=>{
// const rows = [...rowsData];
// rows.splice(index, 1);
// setTableData(rows);
// }
// **********DECLARING FUNCTIONS*********
const handleChange = (evnt) => {
const newInput = (data) => ({ ...data, id: Date.now(), [evnt.target.name]: evnt.target.value })
setformInputData(newInput)
}
const handleSubmit = (evnt) => {
evnt.preventDefault();
setloading(true)
const checkEmptyInput = !Object.values(formInputData).every(res => res === "")
if (checkEmptyInput) {
const newData = (data) => ([...data, formInputData])
setTableData(newData);
const emptyInput = { id: '', Name: '', email: '' }
setformInputData(emptyInput)
}
setTimeout(() => {
setloading(false)
}, 1000)
}
const singleDelete = (event) => {
event.preventDefault();
setloading(true)
const handleDelete = (id) => {
const newArr = [...tableData];
console.log(tableData);
const index = setTableData.findIndex((data) => data.name(id) === id);
console.log(index);
newArr.splice(index, 1);
setTableData(newArr);
}
setTimeout(() => {
setloading(false)
}, 1000)
}
// ************RETURNING VALUES************
return (
<div className="container">
<div className="row">
<div className="col-sm-8">
<div className="col">
<input type="text" onChange={handleChange} value={formInputData.Name} name="Name" className="form-control" placeholder="Name" />
</div>
<div className="col">
<input type="email" onChange={handleChange} value={formInputData.email} name="email" className="form-control" placeholder="Email Address" />
</div>
<div className="col">
<input type="submit" onClick={handleSubmit} className="btn btn-success" />
{
loading ?
<CirclesWithBar
height="75"
width="100"
color="#002B5B"
wrapperStyle={{}}
wrapperClass=""
visible={true}
alignSelf='center'
outerCircleColor=""
innerCircleColor=""
barColor=""
ariaLabel='circles-with-bar-loading' loading={loading} size={50} />
:
<div>
{
<table className="table" id='table'>
<thead>
<tr>
<th>S.N</th>
<th>ID</th>
<th>Full Name</th>
<th>Email Address</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{
tableData.map((data, index) => {
return (
<tr>
<td>{index + 1}</td>
<td>{data.id}</td>
<td>{data.Name}</td>
<td>{data.email}</td>
<td><button value={data.id} onClick={() => singleDelete(data.id)} className="btn btn-danger">Delete</button></td>
</tr>
)
})
}
</tbody>
</table>
}
</div>
}
</div>
</div>
</div>
</div>
);
}
export default Main;
First, the error happens because you don't pass the click event parameter to the function.
It should be like that.
(e) => singleDelete(e, data.id)
Second, You can just use filter method to delete the item by add a condition any element that doesn't have this id.
const singleDelete = (event, id) => {
event.preventDefault();
setloading(true);
setTableData((prev) => prev.filter((i) => i.id !== id));
setTimeout(() => {
setloading(false);
}, 1000);
};
This is a full example of code.
import React from "react";
import { CirclesWithBar } from "react-loader-spinner";
import { useState } from "react";
function Main() {
// *****INITIALIZING*****
const [tableData, setTableData] = useState([]);
const [formInputData, setformInputData] = useState({
id: Date.now(),
Name: "",
email: ""
});
const [loading, setloading] = useState(false);
// const deleteTableRows = (index)=>{
// const rows = [...rowsData];
// rows.splice(index, 1);
// setTableData(rows);
// }
// **********DECLARING FUNCTIONS*********
const handleChange = (evnt) => {
const newInput = (data) => ({
...data,
id: Date.now(),
[evnt.target.name]: evnt.target.value
});
setformInputData(newInput);
};
const handleSubmit = (evnt) => {
evnt.preventDefault();
setloading(true);
const checkEmptyInput = !Object.values(formInputData).every(
(res) => res === ""
);
if (checkEmptyInput) {
const newData = (data) => [...data, formInputData];
setTableData(newData);
const emptyInput = { id: "", Name: "", email: "" };
setformInputData(emptyInput);
}
setTimeout(() => {
setloading(false);
}, 1000);
};
const singleDelete = (event, id) => {
event.preventDefault();
setloading(true);
setTableData((prev) => prev.filter((i) => i.id !== id));
setTimeout(() => {
setloading(false);
}, 1000);
};
// ************RETURNING VALUES************
return (
<div className="container">
<div className="row">
<div className="col-sm-8">
<div className="col">
<input
type="text"
onChange={handleChange}
value={formInputData.Name}
name="Name"
className="form-control"
placeholder="Name"
/>
</div>
<div className="col">
<input
type="email"
onChange={handleChange}
value={formInputData.email}
name="email"
className="form-control"
placeholder="Email Address"
/>
</div>
<div className="col">
<input
type="submit"
onClick={handleSubmit}
className="btn btn-success"
/>
{loading ? (
<CirclesWithBar
height="75"
width="100"
color="#002B5B"
wrapperStyle={{}}
wrapperClass=""
visible={true}
alignSelf="center"
outerCircleColor=""
innerCircleColor=""
barColor=""
ariaLabel="circles-with-bar-loading"
loading={loading}
size={50}
/>
) : (
<div>
{
<table className="table" id="table">
<thead>
<tr>
<th>S.N</th>
<th>ID</th>
<th>Full Name</th>
<th>Email Address</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{tableData.map((data, index) => {
return (
<tr>
<td>{index + 1}</td>
<td>{data.id}</td>
<td>{data.Name}</td>
<td>{data.email}</td>
<td>
<button
value={data.id}
onClick={(e) => singleDelete(e, data.id)}
className="btn btn-danger"
>
Delete
</button>
</td>
</tr>
);
})}
</tbody>
</table>
}
</div>
)}
</div>
</div>
</div>
</div>
);
}
export default Main;
This is working example with codesandbox.
Just modify your singleDelete function in this way -
const singleDelete = (id) => {
setloading(true);
const newTableData = tableData.filter(item => item.id !== id );
setTableData(newTableData)
setTimeout(() => {
setloading(false)
}, 1000)
}
I know this is common question and something is wrong with state, but I still need help with understanding of all features of Redux and Redux-toolkit. So, in my PET project I'm trying to edit an invoice, but UI isn't updating, however it logs to the console changes which you make (here is screenshot).
And if I try to edit items(item name or unit costs or unit) it shows an error.
It's kind of 2 problems in one post, but related to the same topic :)
Now the code.
invoice-slice.js file with editInvoice reducer where might be the problem of updating the state, but i don't know where it can be:
editInvoice(state) {
const existingItem = state.invoices;
existingItem.map((item) => {
if (existingItem.id === item.id) {
return {
id: item.id,
bill_from: item.billFrom,
bill_from_info: item.billFromInfo,
bill_to: item.billTo,
bill_to_info: item.billToInfo,
invoice_num: item.invoiceNumber,
status: item.status,
order_date: item.order_date,
ITEMS: [...item.ITEMS],
};
}
return item;
});
},
EditInvoice.js file:
import React from "react";
import { useParams } from "react-router-dom";
import { useSelector } from "react-redux";
import classes from "./EditInvoice.module.css";
import EditInvoiceItem from "./EditInvoiceItem";
const EditInvoice = () => {
const { invoiceId } = useParams();
const invoices = useSelector((state) => state.invoice.invoices);
const invoice = invoices.find((invoice) => invoice.id === invoiceId);
return invoice ? (
<EditInvoiceItem
invoiceNumber={invoice.invoice_num}
billFrom={invoice.bill_from}
billFromInfo={invoice.bill_from_info}
billTo={invoice.bill_to}
billToInfo={invoice.bill_to_info}
status={invoice.status}
orderDate={invoice.order_date}
items={invoice.ITEMS}
itemName={invoice.item_name}
unitCosts={invoice.unit_costs}
units={invoice.units}
/>
) : (
<div className={classes.centered}>Invoice Not Found.</div>
);
};
export default EditInvoice;
EditInvoiceItem.js file:
import React, { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { uiActions } from "../../store/ui-slice";
import classes from "./AddInvoiceItem.module.css";
import { useFormik } from "formik";
import Wrapper from "../../UI/Wrapper";
import Card from "../../UI/Card";
import Footer from "../../UI/Footer";
import Button from "../../UI/Button";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faCalendar } from "#fortawesome/free-solid-svg-icons";
import { faEllipsis } from "#fortawesome/free-solid-svg-icons";
import { invoiceActions } from "../../store/invoice-slice";
import { useNavigate } from "react-router-dom";
const EditInvoiceItem = (props) => {
const navigate = useNavigate();
const date = new Date();
const options = ["Pending", "Shipped", "Delivered"];
const inputs = [{ item_name: "", unit_costs: "", unit: "" }];
const [startDate, setStartDate] = useState(date);
// const [startDate, setStartDate] = useState(props.orderDate || date);
const [selectedOption, setSelectedOption] = useState(
props.status || options[0]
);
const [listItems, setListItems] = useState(props.items || inputs);
// console.log(props.orderDate.toJSON());
const optionClickHandler = (value) => () => {
setSelectedOption(value);
dispatch(uiActions.toggleMoreOptions());
};
const editInvoiceHandler = (invoice) => {
console.log(invoice);
dispatch(
invoiceActions.editInvoice({
id: invoice.id,
invoiceNumber: invoice.invoiceNumber,
billFrom: invoice.billFrom,
billFromInfo: invoice.billFromInfo,
billTo: invoice.billTo,
billToInfo: invoice.billToInfo,
status: selectedOption,
order_date: startDate.toLocaleDateString(),
ITEMS: [...updateValuesOnSubmit()],
})
);
};
const formikEditInvoice = useFormik({
initialValues: {
invoiceNumber: props.invoiceNumber,
billFrom: props.billFrom,
billFromInfo: props.billFromInfo,
billTo: props.billTo,
billToInfo: props.billToInfo,
status: props.status,
order_date: props.orderDate,
item_name: props.itemName,
unit_costs: props.unitCosts,
units: props.units,
},
onSubmit: (val) => {
editInvoiceHandler(val);
navigate("/invoices", { replace: true });
},
});
const dispatch = useDispatch();
const toggleMoreOptions = () => {
dispatch(uiActions.toggleMoreOptions());
};
const showOtherOptions = useSelector(
(state) => state.ui.selectMoreOptionsIsVisible
);
let counter = 1;
const addItemHandler = () => {
setListItems(listItems.concat({ item_name: "", unit_costs: "", unit: "" }));
};
const updateItemHandler = (index, inputName, value) => {
listItems[index] = { ...listItems[index], [inputName]: value };
};
const updateValuesOnSubmit = () => {
return listItems;
};
const navigateBack = () => {
navigate(-1);
};
return (
<form onSubmit={formikEditInvoice.handleSubmit}>
<Wrapper isShrinked={props.isShrinked}>
<Card>
<div className={classes.content}>
<div className={classes["buttons-wrapper"]}>
<button
type="button"
className={classes["cancel-btn"]}
onClick={navigateBack}
>
Cancel
</button>
<Button>Save</Button>
</div>
<div className={classes["invoice-info-wrapper"]}>
<div className={classes["invoice-info"]}>
<h3>Invoice Info</h3>
<input
placeholder="Number"
type="text"
name="invoiceNumber"
id="invoiceNumber"
onChange={formikEditInvoice.handleChange}
value={formikEditInvoice.values.invoiceNumber}
onBlur={formikEditInvoice.handleBlur}
></input>
</div>
<div className={classes["right-side-column"]}>
<div className={classes["order-status"]}>
<span>Order Status: </span>
<div className={classes.buttons}>
{showOtherOptions && (
<ul className={classes.options}>
{options.map((option, index) => (
<li onClick={optionClickHandler(option)} key={index}>
{option}
</li>
))}
</ul>
)}
<button type="button" className={classes.status}>
{selectedOption}
</button>
<button
type="button"
className={classes.dots}
onClick={toggleMoreOptions}
>
<FontAwesomeIcon icon={faEllipsis} />
</button>
</div>
</div>
<div className={classes["order-date"]}>
<span>Order Date:</span>
<DatePicker
className={classes["order-date-input"]}
selected={startDate}
onChange={(val) => setStartDate(val)}
/>
<FontAwesomeIcon
icon={faCalendar}
className={classes.calendar}
></FontAwesomeIcon>
</div>
</div>
</div>
<div className={classes["order-bills"]}>
<div className={classes["bill-from"]}>
<input
placeholder="Bill From"
type="text"
name="billFrom"
id="billFrom"
onChange={formikEditInvoice.handleChange}
value={formikEditInvoice.values.billFrom}
onBlur={formikEditInvoice.handleBlur}
></input>
<textarea
placeholder="Bill From Info"
name="billFromInfo"
id="billFromInfo"
onChange={formikEditInvoice.handleChange}
value={formikEditInvoice.values.billFromInfo}
onBlur={formikEditInvoice.handleBlur}
></textarea>
</div>
<div className={classes["bill-to"]}>
<input
placeholder="Bill To"
type="text"
name="billTo"
id="billTo"
onChange={formikEditInvoice.handleChange}
value={formikEditInvoice.values.billTo}
onBlur={formikEditInvoice.handleBlur}
></input>
<textarea
placeholder="Bill To Info"
name="billToInfo"
id="billToInfo"
onChange={formikEditInvoice.handleChange}
value={formikEditInvoice.values.billToInfo}
onBlur={formikEditInvoice.handleBlur}
></textarea>
</div>
</div>
<div className={classes["table-wrapper"]}>
<table>
<colgroup>
<col className={classes.col1}></col>
<col className={classes.col2}></col>
<col className={classes.col3}></col>
<col className={classes.col4}></col>
<col className={classes.col5}></col>
<col className={classes.col6}></col>
</colgroup>
<thead>
<tr>
<td className={classes["more-padding"]}>#</td>
<td>Item Name</td>
<td>Unit Costs</td>
<td>Unit</td>
<td>Price</td>
<td></td>
</tr>
</thead>
<tbody>
{listItems.map((item, index) => (
<tr data-1={item} key={index}>
<td className={classes["more-padding"]}>{counter++}</td>
<td>
<input
placeholder="Item Name"
className={classes.inputs}
name="itemName"
id="itemName"
onChange={(e) =>
updateItemHandler(
index,
"item_name",
e.currentTarget.value
)
}
value={item.item_name}
onBlur={formikEditInvoice.handleBlur}
></input>
</td>
<td>
<input
placeholder="Unit Costs"
className={classes.inputs}
name="unitCosts"
id="unitCosts"
onChange={(e) =>
updateItemHandler(
index,
"unit_costs",
e.currentTarget.value
)
}
value={item.unit_costs}
onBlur={formikEditInvoice.handleBlur}
></input>
</td>
<td>
<input
placeholder="Unit"
className={classes.inputs}
name="unit"
id="unit"
onChange={(e) =>
updateItemHandler(
index,
"unit",
e.currentTarget.value
)
}
value={item.unit}
onBlur={formikEditInvoice.handleBlur}
></input>
</td>
<td>0</td>
<td></td>
{/* There should be dynamic values later */}
</tr>
))}
</tbody>
</table>
<div className={classes["add-item-btn"]}>
<button
onClick={addItemHandler}
type="button"
className={classes["add-item-btn"]}
>
Add Item
</button>
</div>
<div className={classes.total}>
<p className={classes["sub-total"]}>
<span>Sub Total: </span>
<span>$0</span>
{/* Dynamic value later here */}
</p>
<div className={classes["total-vat"]}>
<span>Total Vat:</span>
<div className={classes["total-sum"]}>
<span className={classes["input-wrapper"]}>
<input type="text" defaultValue="10"></input>
<span>%</span>
</span>
<span className={classes.sum}>$0</span>
{/* Dynamic value later here */}
</div>
</div>
<div className={classes["grand-total"]}>
<h3>Grand Total</h3>
<div className={classes.input}>
<input type="text" defaultValue="$"></input>
<span>0</span>
{/* Dynamic value later here */}
</div>
</div>
</div>
</div>
<div className={classes.dummy}></div>
</div>
</Card>
<Footer />
</Wrapper>
</form>
);
};
export default EditInvoiceItem;
In my project I'm using Formik to listen for input changes, but in listening to items changes I'm not using it, because i had an issue with that and one guy suggested me that code.
So, I'm facing 2 issues:
UI doesn't update the changes, but i can see in console that changes are made(probably the code in invoice-slice.js is wrong);
When i click and want to change the inputs of Item Name, Unit Costs and Unit i get an error in console and can't change anything.
Please, try helping me and explaining what can cause such problems!
P.S. here is my github repo - https://github.com/stepan-slyvka/test-project
P.P.S. and here is CodeSandbox -
Issues
The main issue is that the editInvoice reducer function isn't consuming an action and its payload, so the passed updated invoice data isn't referenced, and the array mapping result isn't used.
Additionally, the EditInvoice component isn't passing along the invoice id to the EditInvoiceItem (*or EditInvoiceItem isn't capturing it from the path parameters. The invoice.id isn't populated into the form data so it's also not passed along to editInvoiceHandler to be dispatched to the store. This causes the array mapping to fail to find the invoice object that needs to be updated.
Solution
EditInvoice
EditInvoice needs to pass invoice.id as a prop to EditInvoiceItem. It would be quite a bit more clean and easier to maintain to simply pass the entire invoice object though.
const EditInvoice = () => {
const { invoiceId } = useParams();
const invoices = useSelector((state) => state.invoice.invoices);
const invoice = invoices.find((invoice) => invoice.id === invoiceId);
return invoice ? (
<EditInvoiceItem
id={invoice.id} // <-- passed here
invoiceNumber={invoice.invoice_num}
billFrom={invoice.bill_from}
billFromInfo={invoice.bill_from_info}
billTo={invoice.bill_to}
billToInfo={invoice.bill_to_info}
status={invoice.status}
orderDate={invoice.order_date}
items={invoice.ITEMS}
itemName={invoice.item_name}
unitCosts={invoice.unit_costs}
units={invoice.units}
/>
) : (
<div className={classes.centered}>Invoice Not Found.</div>
);
};
EditInvoiceItem
EditInvoiceItem should initialize the form state with the invoice id.
const formikEditInvoice = useFormik({
initialValues: {
id: props.id, // <-- pass id value...
invoiceNumber: props.invoiceNumber,
billFrom: props.billFrom,
billFromInfo: props.billFromInfo,
billTo: props.billTo,
billToInfo: props.billToInfo,
status: props.status,
order_date: props.orderDate,
item_name: props.itemName,
unit_costs: props.unitCosts,
units: props.units,
},
onSubmit: (val) => {
editInvoiceHandler(val); // <-- ...so it's passed along here
navigate("/invoices", { replace: true });
},
});
invoice-slice
The editInvoice case reducer should consume both the current state and the dispatched action so the payload containing the invoice data can be accessed.
editInvoice(state, action) {
const { payload } = action;
state.invoices = state.invoices.map((item) =>
item.id === payload.id
? {
...item,
bill_from: payload.billFrom,
bill_from_info: payload.billFromInfo,
bill_to: payload.billTo,
bill_to_info: payload.billToInfo,
invoice_num: payload.invoiceNumber,
status: payload.status,
order_date: payload.order_date,
ITEMS: payload.ITEMS.slice()
}
: item
);
}
I'm pretty new to javascript, and I am trying to figure out how to calculate sales tax based off of US states. In my code, I attempted to use an if else statement based off of the input value of state to accomplish this. However, no matter what I put in for the value of state the tax is determined based off of 8.75%, and I'm not sure what I am doing wrong. I would really appreciate any help or advice on how to fix this problem.
Thank you
PlaceOrderScreen.js
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link } from 'react-router-dom';
import { createOrder } from '../actions/orderActions';
import CheckoutSteps from '../components/CheckoutSteps';
import { ORDER_CREATE_RESET } from '../constants/orderConstants';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';
export default function PlaceOrderScreen(props) {
const cart = useSelector((state) => state.cart);
if (!cart.paymentMethod) {
props.history.push('/payment');
}
const orderCreate = useSelector((state) => state.orderCreate);
const { loading, success, error, order } = orderCreate;
const toPrice = (num) => Number(num.toFixed(2)); // 5.123 => "5.12" => 5.12
cart.itemsPrice = toPrice(
cart.cartItems.reduce((a, c) => a + c.qty * c.price, 0)
);
//Sales Tax//
{
if (cart.shippingAddress.state === 'New York'||'NY'){
cart.taxPrice = toPrice(0.0875 * cart.itemsPrice)}
else if (cart.shippingAddress.state === 'Kansas'||'KS') {
cart.taxPrice = toPrice(0.065 * cart.itemsPrice)}
else {
cart.taxPrice = toPrice(0 * cart.itemsPrice)}
};
cart.totalPrice = cart.itemsPrice + cart.shippingPrice + cart.taxPrice;
const dispatch = useDispatch();
const placeOrderHandler = () => {
dispatch(createOrder({ ...cart, orderItems: cart.cartItems }));
};
useEffect(() => {
if (success) {
props.history.push(`/order/${order._id}`);
dispatch({ type: ORDER_CREATE_RESET });
}
}, [dispatch, order, props.history, success]);
return (
<div>
<CheckoutSteps step1 step2 step3 step4></CheckoutSteps>
<div className="row top">
<div className="col-2">
<ul>
<li>
<div className="card card-body">
<h2>Shipping</h2>
<p>
<strong>Name:</strong> {cart.shippingAddress.fullName} <br />
<strong>Address: </strong> {cart.shippingAddress.address},
{cart.shippingAddress.city}, {cart.shippingAddress.state}, {cart.shippingAddress.postalCode}
,{cart.shippingAddress.country}
</p>
</div>
</li>
</ul>
</div>
</div>
</div>
ShippingAddressScreen.js
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { saveShippingAddress } from '../actions/cartActions';
import CheckoutSteps from '../components/CheckoutSteps';
export default function ShippingAddressScreen(props) {
const userSignin = useSelector((state) => state.userSignin);
const { userInfo } = userSignin;
const cart = useSelector((state) => state.cart);
const { shippingAddress } = cart;
if (!userInfo) {
props.history.push('/signin');
}
const [fullName, setFullName] = useState(shippingAddress.fullName);
const [address, setAddress] = useState(shippingAddress.address);
const [city, setCity] = useState(shippingAddress.city);
const [state, setState] = useState(shippingAddress.state);
const [postalCode, setPostalCode] = useState(shippingAddress.postalCode);
const [country, setCountry] = useState(shippingAddress.country);
const dispatch = useDispatch();
const submitHandler = (e) => {
e.preventDefault();
dispatch(
saveShippingAddress({ fullName, address, city, state, postalCode, country })
);
props.history.push('/payment');
};
return (
<div>
<CheckoutSteps step1 step2></CheckoutSteps>
<form className="form" onSubmit={submitHandler}>
<div>
<h1>Shipping Address</h1>
</div>
<div>
<label htmlFor="fullName">Full Name</label>
<input
type="text"
id="fullName"
placeholder="Enter full name"
value={fullName}
onChange={(e) => setFullName(e.target.value)}
required
></input>
</div>
<div>
<label htmlFor="address">Address</label>
<input
type="text"
id="address"
placeholder="Enter address"
value={address}
onChange={(e) => setAddress(e.target.value)}
required
></input>
</div>
<div>
<label htmlFor="city">City</label>
<input
type="text"
id="city"
placeholder="Enter city"
value={city}
onChange={(e) => setCity(e.target.value)}
required
></input>
</div>
<div>
<label htmlFor="state">State</label>
<input
type="text"
id="state"
placeholder="Enter state"
value={state}
onChange={(e) => setState(e.target.value)}
required
></input>
</div>
<div>
<label htmlFor="postalCode">Postal Code</label>
<input
type="text"
id="postalCode"
placeholder="Enter postal code"
value={postalCode}
onChange={(e) => setPostalCode(e.target.value)}
required
></input>
</div>
<div>
<label htmlFor="country">Country</label>
<input
type="text"
id="country"
placeholder="Enter country"
value={country}
onChange={(e) => setCountry(e.target.value)}
required
></input>
</div>
<div>
<label />
<button className="primary" type="submit">
Continue
</button>
</div>
</form>
</div>
);
}
Your code should look like this:
cart.shippingAddress.state === 'New York'|| cart.shippingAddress.state === 'NY'
Your current code is testing if the string "NY" is true or not, and that evaluates to true in your boolean test, so you're always getting the 8.75% tax rate.
I'm doing this React.js project and I have to use POST method. I've managed to make the item appear in the the console and in the json file, but it's not showing in my table. I assume It has to do something with the useEffect hook, but since I'm very new to all of this, I don't know how to implement it correctly.
Table
import React, { useState, useEffect } from "react";
import axios from "axios";
const Table = () => {
//FETCH API
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
const fetchPosts = async () => {
setLoading(true);
const res = await axios.get("http://localhost:3000/movies");
setPosts(res.data);
setLoading(false);
};
fetchPosts();
}, []);
//SORTING
const [order, setOrder] = useState("ascending");
const sorting = (s) => {
if (order === "ascending") {
const sorted = [...posts].sort((a, b) =>
a[s].toLowerCase() > b[s].toLowerCase() ? 1 : -1
);
setPosts(sorted);
setOrder("descending");
}
if (order === "descending") {
const sorted = [...posts].sort((a, b) =>
a[s].toLowerCase() < b[s].toLowerCase() ? 1 : -1
);
setPosts(sorted);
setOrder("ascending");
}
};
//TABLE
return (
<div className="container">
<table className="table table-bordered table-striped">
<thead>
<tr>
<th className="click" onClick={() => sorting("name")}>
Name
</th>
<th>Genre</th>
<th>Rating</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{posts.map((post) => (
<tr key={post}>
<td>{post.name}</td>
<td>{post.genre}</td>
<td>{post.rating}</td>
<td>
{<button className="btn btn-outline-success">delete</button>}
{<button className="btn btn-outline-warning">edit</button>}
</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
export default Table;
AddForm
import React from "react";
import { useState } from "react";
function AddForm() {
const [name, setName] = useState("");
const [genre, setGenre] = useState("");
const [rating, setRating] = useState("");
const handleSubmit = (e) => {
fetch("http://localhost:3000/movies", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
name: name,
genre: genre,
rating: rating,
}),
})
.then((response) => response.json())
.then((response) => {
console.log(response);
});
e.preventDefault();
};
return (
<div className="text-center">
<h2>Add a New movie</h2>
<form onSubmit={handleSubmit}>
<label>Name:</label>
<input
name="name"
type="text"
required="required"
onChange={(e) => setName(e.target.value)}
/>
<label>Genre:</label>
<input
name="genre"
type="text"
required="required"
onChange={(e) => setGenre(e.target.value)}
/>
<label>Rating:</label>
<input
name="rating"
type="text"
required="required"
onChange={(e) => setRating(e.target.value)}
/>
<button type="submit">Add</button>
</form>
</div>
);
}
export default AddForm;