I'm new to redux and redux toolkit in React.js. Trying my best to make my PET project to apply for a future job, but faced a problem. I'll try to describe it.
Firstly, the code. removeInvoice function in invoice-slice.js file:
import { createSlice } from "#reduxjs/toolkit";
import { INVOICES_LIST } from "../Pages/Invoice/InvoicesList";
const invoiceSlice = createSlice({
name: "invoice",
initialState: {
invoices: INVOICES_LIST,
},
reducers: {
addNewInvoice(state, action) {
const newItem = action.payload;
state.invoices.push({
id: newItem.id,
billFrom: newItem.bill_from,
billFromAddress: newItem.billFromAddress,
billTo: newItem.bill_to,
billToAddress: newItem.bill_to_address,
invoiceNumber: newItem.invoice_num,
});
console.log(newItem);
},
removeInvoice(state, action) {
const id = action.payload;
state.invoices = state.invoices.filter((item) => item.id !== id);
console.log(action);
console.log(state.invoices);
},
editInvoice() {},
},
});
export const invoiceActions = invoiceSlice.actions;
export default invoiceSlice;
INVOICES_LIST looks like this:
export const INVOICES_LIST = [
{
id: Math.random().toString(),
number: Math.random().toFixed(2),
invoice_num: "#1232",
bill_from: "Pineapple Inc.",
bill_to: "REDQ Inc.",
total_cost: "14630",
status: "Pending",
order_date: "February 17th 2018",
bill_from_email: "pineapple#company.com",
bill_from_address: "86781 547th Ave, Osmond, NE, 68765",
bill_from_phone: "+(402) 748-3970",
bill_from_fax: "",
bill_to_email: "redq#company.com",
bill_to_address: "405 Mulberry Rd, Mc Grady, NC, 28649",
bill_to_phone: "+(740) 927-9284",
bill_to_fax: "+0(863) 228-7064",
ITEMS: {
item_name: "A box of happiness",
unit_costs: "200",
unit: "14",
price: "2800",
sub_total: "133300",
vat: "13300",
grand_total: "14630",
},
},
{
id: Math.random().toString(),
number: Math.random().toFixed(2),
invoice_num: "#1232",
bill_from: "AMD Inc.",
bill_to: "Intel Inc.",
total_cost: "14630",
status: "Delivered",
order_date: "February 17th 2018",
bill_from_email: "pineapple#company.com",
bill_from_address: "86781 547th Ave, Osmond, NE, 68765",
bill_from_phone: "+(402) 748-3970",
bill_from_fax: "",
bill_to_email: "redq#company.com",
bill_to_address: "405 Mulberry Rd, Mc Grady, NC, 28649",
bill_to_phone: "+(740) 927-9284",
bill_to_fax: "+0(863) 228-7064",
ITEMS: {
item_name: "Unicorn Tears",
unit_costs: "500",
unit: "14",
price: "1700",
sub_total: "133300",
vat: "13300",
grand_total: "14630",
},
},
{
id: Math.random().toString(),
number: Math.random().toFixed(2),
invoice_num: "#1232",
bill_from: "Apple Inc.",
bill_to: "Samsung",
total_cost: "14630",
status: "Shipped",
order_date: "February 17th 2018",
bill_from_email: "pineapple#company.com",
bill_from_address: "86781 547th Ave, Osmond, NE, 68765",
bill_from_phone: "+(402) 748-3970",
bill_from_fax: "",
bill_to_email: "redq#company.com",
bill_to_address: "405 Mulberry Rd, Mc Grady, NC, 28649",
bill_to_phone: "+(740) 927-9284",
bill_to_fax: "+0(863) 228-7064",
ITEMS: {
item_name: "Rainbow Machine",
unit_costs: "700",
unit: "5",
price: "3500",
sub_total: "133300",
vat: "13300",
grand_total: "14630",
},
},
];
AllInvoices.js file where i map invoices:
import React, { Fragment } from "react";
import { useDispatch, useSelector } from "react-redux";
import { uiActions } from "../../store/ui-slice";
// import { invoiceActions } from "../../store/invoice-slice";
import { INVOICES_LIST } from "../Invoice/InvoicesList";
import Wrapper from "../../UI/Wrapper";
import Card from "../../UI/Card";
import Footer from "../../UI/Footer";
import Button from "../../UI/Button";
// import InvoiceItemDescription from "./InvoiceItemDescription";
// import EditInvoiceItem from "./EditInvoiceItem";
import classes from "./AllInvoices.module.css";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faChevronDown } from "#fortawesome/free-solid-svg-icons";
// import AddInvoiceItem from "./AddInvoiceItem";
import { Link } from "react-router-dom";
import Invoice from "./Invoice";
const AllInvoices = (props) => {
// const { id } = props;
const dispatch = useDispatch();
const toggleSelectOptions = () => {
dispatch(uiActions.toggleSelectOptions());
};
// const removeInvoiceItem = (id) => {
// dispatch(invoiceActions.removeInvoice(id));
// };
const showMoreOptions = useSelector(
(state) => state.ui.selectOptionsIsVisible
);
// const invoice = useSelector((state) => state.invoices);
return (
<Fragment>
<Wrapper isShrinked={props.isShrinked}>
<Card>
<h1 className={classes.header}>Invoice</h1>
<div className={classes.content}>
<div className={classes["btn-wrapper"]}>
<Link to="/invoices/add-invoice">
<Button>Add Invoice</Button>
</Link>
</div>
<div className={classes.invoices}>
{showMoreOptions && (
<ul className={classes.list}>
<li>Select all invoices</li>
<li>Unselect all</li>
<li>Delete selected</li>
</ul>
)}
<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>
<col className={classes.col7}></col>
</colgroup>
<thead className={classes["table-head"]}>
<tr>
<th className={classes.position}>
<span className={classes.checkbox}>
<input type="checkbox"></input>
</span>
<FontAwesomeIcon
icon={faChevronDown}
className={classes.chevron}
onClick={toggleSelectOptions}
/>
</th>
<th>
<span className={classes["thead-text"]}>Number</span>
</th>
<th>
<span className={classes["thead-text"]}>Bill From</span>
</th>
<th>
<span className={classes["thead-text"]}>Bill To</span>
</th>
<th>
<span className={classes["thead-text"]}>Total Cost</span>
</th>
<th>
<span className={classes["thead-text"]}>Status</span>
</th>
</tr>
</thead>
<tbody>
{INVOICES_LIST.map((invoice, index) => (
<Invoice
key={index}
invoiceItem={{
id: invoice.id,
invoice_num: invoice.invoice_num,
bill_from: invoice.bill_from,
bill_to: invoice.bill_to,
status: invoice.status,
}}
/>
))}
</tbody>
</table>
</div>
</div>
</Card>
<Footer />
</Wrapper>
</Fragment>
);
};
export default AllInvoices;
And Invoice.js file where i should use removeInvoice:
import React from "react";
import classes from "./Invoice.module.css";
import { useDispatch } from "react-redux";
import { Link } from "react-router-dom";
import { invoiceActions } from "../../store/invoice-slice";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faTrash } from "#fortawesome/free-solid-svg-icons";
const Invoice = (props) => {
const { id, invoice_num, bill_from, bill_to, status } = props.invoiceItem;
const dispatch = useDispatch();
const removeInvoiceItem = () => {
dispatch(invoiceActions.removeInvoice(id));
};
return (
<tr className={classes.height}>
<td>
<span className={classes.checkbox}>
<input type="checkbox"></input>
</span>
</td>
<td>
<span>{invoice_num}</span>
</td>
<td>
<span>{bill_from}</span>
</td>
<td>
<span>{bill_to}</span>
</td>
<td>
<span>14300</span>
{/* This should be a dynamic value later */}
</td>
<td>
<span
className={`${
status === "Pending" ? classes["status-pending"] : ""
} ${status === "Delivered" ? classes["status-delivered"] : ""} ${
status === "Shipped" ? classes["status-shipped"] : ""
}`}
>
{status}
</span>
</td>
<td>
<div className={classes.buttons}>
<Link to={`/invoices/invoice-description/${id}`}>
<button className={classes["view-btn"]}>View</button>
</Link>
<button className={classes["delete-btn"]} onClick={removeInvoiceItem}>
<FontAwesomeIcon icon={faTrash} />
</button>
</div>
</td>
</tr>
);
};
export default Invoice;
Now, the issue. It seems to remove the invoice from an array, because array changes from 3 items to 2, and shows in a console payload with appropriate id of item which i wanted to remove by clicking on a button, but UI console.log here doesn't reflect the changes, there is still 3 invoice items instead of 2 or less. Anyone know what can be the problem?
I've already tried alot of variants, like to pass an id to a function like this:
const removeInvoiceItem = (id) => {
dispatch(invoiceActions.removeInvoice(id));
};
Tried to make it with curly braces:
const removeInvoiceItem = (id) => {
dispatch(invoiceActions.removeInvoice({ id }));
};
Also tried anonymous function here:
<button className={classes["delete-btn"]} onClick={() => removeInvoiceItem(id)}>
<FontAwesomeIcon icon={faTrash} />
</button>
And so on. I know that if UI doesn't change, then state is not changing, but, in my case, i overwrite the state like this:
state.invoices = state.invoices.filter((item) => item.id !== id);
So, i don't know what else to do. Thought of useSelector and tried it like this:
const invoice = useSelector((state) => state.invoices);
And in map function:
{invoice.map((invoice, index) => (
<Invoice
key={index}
invoiceItem={{
id: invoice.id,
invoice_num: invoice.invoice_num,
bill_from: invoice.bill_from,
bill_to: invoice.bill_to,
status: invoice.status,
}}
/>
))}
But it was crashing the page and telling me this error: Uncaught TypeError: invoice.map is not a function.
So, i don't know what to do else. Please, help me!!!
P.s. I'm new in stackoveflow, so sorry if something wrong :)
The problem is that you're using the constant INVOICE_LIST to map your elements instead of the current state of the store.
You used INVOICE_LIST to initialize your slice, that's good. But then you did not use what you initialized, you simply used the constant, and that's why the UI remained constant.
You should use useSelector to access that state like so:
const invoiceList = useSelector(state => state.invoice.invoices)
This should be the correct syntaxe in your case:
state.sliceName.wantedProperty
Now when you map on "invoiceList" instead of "INVOICE_LIST", this should do the trick!
You aren't rerendering the INVOICE_LIST when it changes. You would want to have a useEffect or something similar that will rerender the component when the INVOICE_LIST changes to see any changes on the UI side.
Your problem is this:
{INVOICES_LIST.map((invoice, index) => (
<Invoice
key={index}
invoiceItem={{
id: invoice.id,
invoice_num: invoice.invoice_num,
bill_from: invoice.bill_from,
bill_to: invoice.bill_to,
status: invoice.status,
}}
/>
))}
This is rendering static content and will not change even when you make a change to the redux store. You need to change this to state that will rerender when it changes.
Related
I am working on a personal project where NFL Data is displayed by team. I am just learning React and would like to know how to use props and map image urls from an array to display multiple NFL logo cards. I have made a similar website using strictly css, html, and javascript but need to do it in react, anyways, this is what I have:
Home.js
import React from "react"
import { Link} from "react-router-dom"
import Box from '#material-ui/core/Box';
const teams = [
{
id: 1,
teamName: "Kansas City Cheifs",
urlImage: "public/chiefs_logo.jpg"
},
{
id: 2,
teamName: "Cincinatti Bengals",
urlImage: "public/Bengals.jpg"
},
{
id: 3,
teamName: "Denver Broncos",
urlImage: "public/Denver-Broncos-symbol.jpeg"
},
{
id: 4,
teamName: "Carolina Panthers",
urlImage: "public/panthers.png"
}
];
export default function Home(props) {
return (
<div className="Team-Box">
const teamCards = teams.map(team => )
<Box className="Box" key={teams.id} background-image={props.urlImage}/>
<Box className="Box" background-image={props.urlImage}/>
<Link to="/Home"></Link>
</div>
)
}
What it looks like so far
[What I want it to look like][2]
[2]: https://i.stack.imgur.com/KK0tw.jpg, except for all 32 NFL teams
Inside of your return you want something like this.
return (
<div>
{teams.map((team) => (
<div key={team.id} className='Team-Box'>
<Box
className='Box'
style={{ backgroundImage: `url(${team.imageUrl})` }}
/>
</div>
))}
<Link to='/Home'></Link>
</div>
);
Here is an idea of what this would look like if you wanted to pass some data as props to a Card component responsible for displaying the information on each team.
import { useState } from 'react';
const initialTeams = [
{
id: 1,
teamName: 'Kansas City Chiefs',
urlImage: 'public/chiefs_logo.jpg',
},
{
id: 2,
teamName: 'Cincinatti Bengals',
urlImage: 'public/Bengals.jpg',
},
{
id: 3,
teamName: 'Denver Broncos',
urlImage: 'public/Denver-Broncos-symbol.jpeg',
},
{
id: 4,
teamName: 'Carolina Panthers',
urlImage: 'public/panthers.png',
},
];
const Card = ({ imageUrl, teamName }) => (
<div className='team-card' style={{ backgroundImage: `url(${imageUrl})` }}>
{teamName}
</div>
);
const Home = () => {
const [teams, setTeams] = useState(initialTeams);
return (
<div>
{teams.map(({ id, imageUrl, teamName }) => (
<Card key={id} imageUrl={imageUrl} teamName={teamName} />
))}
</div>
);
};
export default Home;
I am facing a bit of a problem. I have two components. One parent component that is called goals and under different instances of goal. I am updating state of the parent component by passing a function to its children. The state of the parent is updated however only one of the child who made the change apply the change they should be all the same
Goals
import UserGoal from "./UserGoal";
import { Button, Container, Form, InputGroup } from "react-bootstrap";
import React, { useState, useEffect } from "react";
import "./UserGoal.css";
import { Link } from "react-router-dom";
const UserGoals = () => {
var test = [
{
id: 1,
name: "Marriage",
icon: "Marriage.png",
isSelected:false,
style: "goal-category"
},
{
id: 2,
name: "Car",
icon: "Car.png",
isSelected:false,
style: "goal-category"
},
{
id: 3,
name: "Home",
icon: "Home.png",
isSelected:false,
style: "goal-category"
},
{
id: 4,
name: "Education",
icon: "Education.png",
isSelected:false,
style: "goal-category"
},
{
id: 5,
name: "Travel",
icon: "Travel.png",
isSelected:false,
style: "goal-category"
},
{
id: 6,
name: "Retirement",
icon: "Retirement.png",
isSelected:false,
style: "goal-category"
},
{
id: 7,
name: "Other",
icon: "Other.png",
isSelected:false,
style: "goal-category"
},
]
const [goals, setGoals] = useState(test)
const changeSelection = (id) => {
const newarr = goals.map((GoalType)=> {
if(GoalType.id===id){
GoalType.isSelected=true
GoalType.style= "goal-category2"
}
else {
GoalType.isSelected=false
GoalType.style= "goal-category"
}
return GoalType
})
forceUpdate(newarr)
console.log(goals);
}
const forceUpdate = React.useCallback((arr) => {
setGoals(arr);
}, []);
return (
<Container>
<h1>Your saving goal</h1>
<div className="goal-categories">
{goals.map((GoalType) => (
<UserGoal
id={GoalType.id}
name={GoalType.name}
icon={GoalType.icon}
isSelected={()=>GoalType.isSelected}
style={()=>GoalType.style}
key={GoalType.id}
onClick={changeSelection}
></UserGoal>
))}
</div>
<div className="reg-wrapper">
<div className="reg-inner">
<Form className="ctr-form">
<Form.Label>Amount</Form.Label>
<InputGroup className="mb-3">
<Form.Control
className="input"
type="number"
placeholder="0.00"
/>
<div className="input-group-text">SAR</div>
</InputGroup>
<Form.Label>Date</Form.Label>
<InputGroup className="mb-3">
<Form.Control className="input" type="date" />
</InputGroup>
<div className="btn">
<Link to="/expenses">
<Button id="btn btn-primary" className="dbtn">
Next
</Button>
</Link>
</div>
</Form>
</div>
</div>
</Container>
);
}
export default UserGoals
Goal
import React from "react";
import { useState } from "react";
import "./UserGoal.css";
import { useEffect } from "react";
export default function UserGoal({ id, name, icon, isSelected, style, onClick }) {
const [style_, setStyle_] = useState(style())
return (
<>
<div className="goal-category-container">
<div
onClick={()=>{
onClick(id);
setStyle_(style())
}}
className={style_}
>
<div className="icon-wrap">
<img src={"assets/" + icon} alt="Goal Icon" />
</div>
</div>
<div className="category-name">{name} </div>
</div>
</>
);
}
You call setStyle_ immediately after the callback, even though the effects of the callback are asynchronous. Ie, the style won't be updated yet because the patent is doing an asynchronous setState.
Instead send down style as a value, not a function. That way the child will react to updates. Also having style as state oh the child is unnecessary, since you can just rely on the prop.
Also, don't use mutation in your map function. Instead create new goal objects. And your React.useCallback is equivalent to using the setter directly, it can be removed.
I'm building a CRUD app in React (similar to the todo apps on the net), however the main difference is I am appending each entry to a bootstrap Table. This might not be the best way to represent the data, but it helps me get off the ground quickly without having to go over some CSS styling, which I'm pretty weak at.
My goal is to be able to delete, after a warning prompt, any given entry from the Table. However, I am not sure of the best way to do this. So here is the Form to enter a player's name:
import React, { useState } from "react";
import { Form, Button } from "react-bootstrap";
const PlayerForm = ({ addPlayer }) => {
const [name, setName] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
addPlayer(name);
setName("");
};
return (
<Form onSubmit={handleSubmit}>
<Form.Group controlId="name">
<Form.Label>Name</Form.Label>
<Form.Control
required
type="text"
value={name}
placeholder="Normal text"
onChange={(e) => setName(e.target.value)}
/>
</Form.Group>
<Button type="submit">Add Player</Button>
</Form>
);
};
export default PlayerForm;
And after that, the data goes into a PlayerList component:
import React, { useState } from "react";
import { v4 as uuidv4 } from "uuid";
import { Table, Form } from "react-bootstrap";
import PlayerForm from "./PlayerForm";
import styled from "styled-components";
const StyledInput = styled(Form.Control)`
padding: 2px 5px;
width: 60px;
height: 30px;
`;
const PlayerList = () => {
const [players, setPlayers] = useState([
{ name: "Harry Kane", country: "England", id: 1, goals: 0, percentage: 0 },
{
name: "Marcus Rashford",
country: "England",
id: 2,
goals: 0,
percentage: 0,
},
]);
const addPlayer = (name) => {
setPlayers([
...players,
{ name: name, id: uuidv4(), country: "England", goals: 0, percentage: 0 },
]);
};
return (
<div>
<Table>
<thead>
<tr>
<th>Name</th>
<th>Total Goals</th>
<th>Goal Percentage</th>
</tr>
</thead>
<tbody>
{players.map((player) => (
<tr key={player.id}>
<td>{player.name}</td>
<td>
<Form>
<Form.Group controlId="goals">
<StyledInput
size="sm"
required
type="number"
defaultValue={player.goals}
step={1}
className="smaller-input"
min="0"
/>
</Form.Group>
</Form>
</td>
<td>
<Form>
<Form.Group controlId="goals">
<StyledInput
size="sm"
required
type="number"
defaultValue={player.percentage}
step={1}
className="smaller-input"
min="0"
/>
</Form.Group>
</Form>
</td>
</tr>
))}
</tbody>
</Table>
<PlayerForm addPlayer={addPlayer} />
</div>
);
};
export default PlayerList;
Now I would like to be able to delete any given player after adding them to the Table. I'm not sure how to proceed beyond building a function
const removePlayer =() =>{
}
Please could someone help me with this?
first, you have to send the player id to the function then filter out that player
const removePlayer = (playerId) => {
setPlayer(player.filter(p => p.id !== playerId));
}
I am passing a list of articles via const ARTICLES from index.js to App.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import registerServiceWorker from "./registerServiceWorker";
import { applyPolyfills, defineCustomElements } from "h8k-components/loader";
const ARTICLES = [
{
title: "A message to our customers",
upvotes: 12,
date: "2020-01-24",
},
{
title: "Alphabet earnings",
upvotes: 22,
date: "2019-11-23",
},
{
title: "Artificial Mountains",
upvotes: 2,
date: "2019-11-22",
},
{
title: "Scaling to 100k Users",
upvotes: 72,
date: "2019-01-21",
},
{
title: "the Emu War",
upvotes: 24,
date: "2019-10-21",
},
{
title: "What's SAP",
upvotes: 1,
date: "2019-11-21",
},
{
title: "Simple text editor has 15k monthly users",
upvotes: 7,
date: "2010-12-31",
},
];
ReactDOM.render(<App articles={ARTICLES} />, document.getElementById("root"));
registerServiceWorker();
applyPolyfills().then(() => {
defineCustomElements(window);
});
I then pass the same list of articles from App.js to components/Article.js using React Hooks to update the order of the articles based on their upvotes and date properties when their respective buttons are clicked. But the update never happens. I can see the articleList changes when the buttons are clicked via console.log, but the Article.js component does not re-render these changes.
import React, { useState } from "react";
import "./App.css";
import "h8k-components";
import Articles from "./components/Articles";
const title = "Sorting Articles";
function App({ articles }) {
const [articleList, setArticle] = useState(articles);
function onTopClicked() {
setArticle(
articleList.sort(function (a, b) {
return b.upvotes - a.upvotes;
})
);
console.log("top", articleList);
}
function onNewestClicked() {
setArticle(
articleList.sort(function (a, b) {
let dateA = new Date(a.date);
let dateB = new Date(b.date);
return dateB - dateA;
})
);
console.log("date", articleList);
}
return (
<div className="App">
<h8k-navbar header={title}></h8k-navbar>
<div className="layout-row align-items-center justify-content-center my-20 navigation">
<label className="form-hint mb-0 text-uppercase font-weight-light">
Sort By
</label>
<button
data-testid="most-upvoted-link"
onClick={onTopClicked}
className="small"
>
Most Upvoted
</button>
<button
onClick={onNewestClicked}
data-testid="most-recent-link"
className="small"
>
Most Recent
</button>
</div>
<Articles articles={articleList} />
</div>
);
}
export default App;
The Article.js component receives the articleList data via its props.
import React from "react";
function Articles({ articles }) {
return (
<div className="card w-50 mx-auto">
<table>
<thead>
<tr>
<th>Title</th>
<th>Upvotes</th>
<th>Date</th>
</tr>
</thead>
<tbody>
{articles.map((article, i) => (
<tr key={i} data-testid="article" key="article-index">
<td data-testid="article-title">{article.title}</td>
<td data-testid="article-upvotes">{article.upvotes}</td>
<td data-testid="article-date">{article.date}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
export default Articles;
Why won't the articles re-render the articleList data when the buttons from App.js are clicked so I can re-order my articles based on their upvotes and date properties?
The sort function does not create return a new array. It sorts in place and returns the same array. So the reference is still the same and the state is not updated.
You should make a copy of the sorted array and set it as a new state
setArticle(
[...articleList.sort(function (a, b) {
let dateA = new Date(a.date);
let dateB = new Date(b.date);
return dateB - dateA;
})]
);
I'd like to, when a user deletes on row, for only that row to be deleted. Currently that only happens sometimes. And when you have only two items left to delete, when you click on the delete button, the row's data toggles and replaces itself. It doesn't actually delete.
mainCrud.js - houses the add and delete
crudAdd.js - defines state, event handlers, renders the form itself
crudTable.js - maps pre-defined rows defined in mainCrud.js, renders the table itself
Link to CodeSandbox (tables are under campaigns, dev and news tabs).
Any idea what could be causing this?
MainCrud.js
import React, { useState } from "react";
import CrudIntro from "../crud/crudIntro/crudIntro";
import CrudAdd from "../crud/crudAdd/crudAdd";
import CrudTable from "../crud/crudTable/crudTable";
const MainCrud = props => {
// Project Data
const projectData = [
{
id: 1,
name: "Skid Steer Loaders",
description:
"To advertise the skid steer loaders at 0% financing for 60 months.",
date: "February 1, 2022"
},
{
id: 2,
name: "Work Gloves",
description: "To advertise the work gloves at $15.",
date: "February 15, 2022"
},
{
id: 3,
name: "Telehandlers",
description: "To advertise telehandlers at 0% financing for 24 months.",
date: "March 15, 2022"
}
];
const [projects, setProject] = useState(projectData);
// Add Project
const addProject = project => {
project.id = projectData.length + 1;
setProject([...projects, project]);
};
// Delete Project
const deleteProject = id => {
setProject(projectData.filter(project => project.id !== id));
};
return (
<div>
<section id="add">
<CrudIntro title={props.title} subTitle={props.subTitle} />
<CrudAdd addProject={addProject} />
</section>
<section id="main">
<CrudTable projectData={projects} deleteProject={deleteProject} />
</section>
</div>
);
};
export default MainCrud;
CrudAdd.js
import React, { Component } from "react";
import "../crudAdd/crud-add.scss";
import "../../button.scss";
class CrudAdd extends Component {
state = {
id: null,
name: "",
description: "",
date: ""
};
handleInputChange = e => {
let input = e.target;
let name = e.target.name;
let value = input.value;
this.setState({
[name]: value
});
};
handleFormSubmit = e => {
e.preventDefault();
this.props.addProject({
id: this.state.id,
name: this.state.name,
description: this.state.description,
date: this.state.date
});
this.setState({
// Clear values
name: "",
description: "",
date: ""
});
};
render() {
return (
<div>
<form onSubmit={this.handleFormSubmit}>
<input
name="name"
type="name"
placeholder="Name..."
id="name"
value={this.state.name}
onChange={e => this.setState({ name: e.target.value })}
required
/>
<input
name="description"
type="description"
placeholder="Description..."
id="description"
value={this.state.description}
onChange={e => this.setState({ description: e.target.value })}
required
/>
<input
name="date"
type="name"
placeholder="Date..."
id="date"
value={this.state.date}
onChange={e => this.setState({ date: e.target.value })}
required
/>
<button type="submit" className="btn btn-primary">
Add Project
</button>
</form>
</div>
);
}
}
export default CrudAdd;
CrudTable.js
import React, { Component } from "react";
import "../crudTable/crud-table.scss";
class CrudTable extends Component {
render() {
const props = this.props;
return (
<div>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th scope="col">Project Name</th>
<th scope="col">Project Description</th>
<th scope="col">Date</th>
<th scope="col"> </th>
</tr>
</thead>
<tbody>
{props.projectData.length > 0 ? (
props.projectData.map(project => (
<tr key={project.id}>
<td>{project.name}</td>
<td>{project.description}</td>
<td>{project.date}</td>
<td>
<button className="btn btn-warning">Edit</button>
<button
onClick={() => props.deleteProject(project.id)}
className="btn btn-danger"
>
Delete
</button>
</td>
</tr>
))
) : (
<tr>
<td>No projects found. Please add a project.</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
);
}
}
export default CrudTable;
This is because you are filtering over projectData. Update your deleteProject method to filter over your React.useState projects variable and it will work.
const deleteProject = id => {
setProject(projects.filter(project => project.id !== id));
};
See code sandbox example here.