Show array data in Table rows in ReactJS - javascript

How can I output that productName inside that return? The items are from the selected cart items. The const order (function) does show in the console, but I can't get it to show inside the return of render.
const mapState = ({ user }) => ({
currentUser: user.currentUser,
});
const mapCartItems = createStructuredSelector({
items: selectCartItems,
});
const CheckingOut = (product) => {
const { total, cartCount, items } = useSelector(mapCartItems);
const order = {
orderItems: items.map((item) => {
const { productName, qty} = item;
return {
productName,
};
console.log(item.productName); // this one is showing in the console
}),
};
return (
<div>
<Container fixed>
<table >
<tr>
<td>Name : </td> // like show the items here
<td></td>
</tr>
<br></br>
<tr>
<td>
<PaymentIcon
style={{ marginRight: "1rem", color: " #e31837" }}
/>
Total Price:
</td>
<td>₱{total}.00</td>
</tr>
</table>
//just other codes for submit
</Container>
</div>
);
};
export default CheckingOut;

Assuming items is an array similar to:
[
{ ID: 1, productName: 'p1', qty: 10 },
{ ID: 2, productName: 'p2', qty: 15 },
]
You can show productName and qty of each item in this array in <tr> and <td> of a table as:
{items.map((item) => (
<tr key={item.ID}>
<td>Name: {item.productName}</td>
<td>{item.qty}</td>
</tr>
))}
This will work even when items is empty array i.e. []. In case, it is null or undefined, you may do optional chaining.
It will show something like this in the table at UI:
Name: p1
10
Name: p2
15
PS: Above solution doesn't really require a separate function order as shown in your question.

const order = (product) => {
const order = {
orderItems: items.map((item) => {
const { productName, qty} = item;
return {
productName,
};
}),
};
order.orderItems.forEach((item) => console.log(item.productName)) // will log all productName
return order.orderItems
}
Function order will return order.orderItems which has array of objects. For example:
order :{
orderItems: [
{
productName: 'car',
},
{
productName: 'bike',
}
]
}
To access each of product name you have to iterate throught Array nested in order.orderItems
order(product).forEach((item) => {
console.log(item.productName) // car, bike
});

EDITED
// WHITHOUT STATE
const order = (product) => {
const order = {
orderItems: items.map((item) => {
const { productName, qty} = item;
return {
productName,
};
})
};
return order; // Add this line to return your order object
};
// WITH STATE
const [myOrders, setMyOrders] = useState({});
const order = (product) => {
setMyOrders({
orderItems: items.map((item) => {
const { productName, qty} = item;
return {
productName,
};
})
});
return //some layout;
};
Then in the return you can access like this:
// WITHOUT STATE
return order().orderItems.map(item =>
whatever you want to do with item.productName...
);
// WITH STATE
return myOrders.orderItems.map(item =>
whatever code you want to add...
);

You can map over arrays and return each element of the array inside a jsx element. If you had an array:
const arr = [1,2,3,4,5]
You could map over them and return them as:
arr?.map(num => (<p> {num} </p>))
Likewise, you can do:
return(
order?.orderItems?.map(item => (
<p>{item.productName} </p>
))
)

Related

When using a UseState with an array, React doesn't update my component render on screen after using setTimeout [duplicate]

I have retrieved data stored using useState in an array of object, the data was then outputted into form fields. And now I want to be able to update the fields (state) as I type.
I have seen examples on people updating the state for property in array, but never for state in an array of object, so I don't know how to do it. I've got the index of the object passed to the callback function but I didn't know how to update the state using it.
// sample data structure
const data = [
{
id: 1,
name: 'john',
gender: 'm'
}
{
id: 2,
name: 'mary',
gender: 'f'
}
]
const [data, setData] = useState([]);
const updateFieldChanged = index => e => {
console.log('index: ' + index);
console.log('property name: '+ e.target.name);
setData() // ??
}
return (
<React.Fragment>
{data.map((data, index) => {
<li key={data.name}>
<input type="text" name="name" value={data.name} onChange={updateFieldChanged(index)} />
</li>
})}
</React.Fragment>
)
Here is how you do it:
// sample data structure
/* const data = [
{
id: 1,
name: 'john',
gender: 'm'
}
{
id: 2,
name: 'mary',
gender: 'f'
}
] */ // make sure to set the default value in the useState call (I already fixed it)
const [data, setData] = useState([
{
id: 1,
name: 'john',
gender: 'm'
}
{
id: 2,
name: 'mary',
gender: 'f'
}
]);
const updateFieldChanged = index => e => {
console.log('index: ' + index);
console.log('property name: '+ e.target.name);
let newArr = [...data]; // copying the old datas array
// a deep copy is not needed as we are overriding the whole object below, and not setting a property of it. this does not mutate the state.
newArr[index] = e.target.value; // replace e.target.value with whatever you want to change it to
setData(newArr);
}
return (
<React.Fragment>
{data.map((datum, index) => {
<li key={datum.name}>
<input type="text" name="name" value={datum.name} onChange={updateFieldChanged(index)} />
</li>
})}
</React.Fragment>
)
The accepted answer leads the developer into significant risk that they will mutate the source sequence, as witnessed in comments:
let newArr = [...data];
// oops! newArr[index] is in both newArr and data
// this might cause nasty bugs in React.
newArr[index][propertyName] = e.target.value;
This will mean that, in some cases, React does not pick up and render the changes.
The idiomatic way of doing this is by mapping your old array into a new one, swapping what you want to change for an updated item along the way.
setData(
data.map(item =>
item.id === index
? {...item, someProp : "changed", someOtherProp: 42}
: item
))
setDatas(datas=>({
...datas,
[index]: e.target.value
}))
with index being the target position and e.target.value the new value
You don't even need to be using the index ( except for the key if you want ) nor copying the old datas array,and can even do it inline or just pass data as an argument if you prefer updateFieldChanged to not be inline. It's done very quickly that way :
const initial_data = [
{
id: 1,
name: "john",
gender: "m",
},
{
id: 2,
name: "mary",
gender: "f",
},
];
const [datas, setDatas] = useState(initial_data);
return (
<div>
{datas.map((data, index) => (
<li key={index}>
<input
type="text"
value={data.name}
onChange={(e) => {
data.name = e.target.value;
setDatas([...datas]);
}}
/>
</li>
))}
</div>
);
};
This is what I do:
const [datas, setDatas] = useState([
{
id: 1,
name: "john",
gender: "m",
},
{
id: 2,
name: "mary",
gender: "f",
},
]);
const updateFieldChanged = (name, index) => (event) => {
let newArr = datas.map((item, i) => {
if (index == i) {
return { ...item, [name]: event.target.value };
} else {
return item;
}
});
setDatas(newArr);
};
return (
<React.Fragment>
{datas.map((data, index) => {
<li key={data.name}>
<input
type="text"
name="name"
value={data.name}
onChange={updateFieldChanged("name", index)}
/>
</li>;
<li key={data.gender}>
<input
type="text"
name="gender"
value={data.gender}
onChange={updateFieldChanged("gender", index)}
/>
</li>;
})}
</React.Fragment>
);
Spread the array before that. As you cannot update the hook directly without using the method returned by useState
const newState = [...originalState]
newState[index] = newValue
setOriginalState(newState)
This will modify the value of the state and update the useState hook if its an array of string.
const updateFieldChanged = index => e => {
name=e.target.name //key
let newArr = [...data]; // copying the old datas array
newArr[index][name] = e.target.value; //key and value
setData(newArr);
}
return (
<React.Fragment>
{data.map((datum, index) => {
<li key={datum.name}>
<input type="text" name="name" value={datum.name} onChange={updateFieldChanged(index)} />
</li>
})}
</React.Fragment>
)
const [datas, setDatas] = useState([ { id: 1, name: 'john', gender: 'm' } { id: 2, name: 'mary', gender: 'f' } ]);
const updateFieldChanged = (index, e) => { const updateData = { ...data[index], name: e.target.name }
setData([...data.slice(0, index), updateData, ...data.slice(index + 1)]) }
I am late to reply but I had also same problem, so I got solution through this query. Have a look on it if it can help you.The example that I did is that I have a state , named OrderList, and so a setter for it is SetOrderList. To update a specific record in a list, am using SetOrderList and passing it a map function with that list of which I need to change, so I will compare Index or Id of my list, where it will match, I will change that specific record.
const Action = (Id, Status) => { //`enter code here`Call this function onChange or onClick event
setorderList([...orderList.map((order) =>
order.id === Id ? { ...order, status: Status } : order
),
]);
}
complete example for update value based on index and generate input based on for loop....
import React, { useState,useEffect } from "react";
export default function App() {
const [datas, setDatas] =useState([])
useEffect(() => {
console.log("datas");
console.log(datas);
}, [datas]);
const onchangeInput = (val, index) =>{
setDatas(datas=>({
...datas,
[index]: val.target.value
}))
console.log(datas);
}
return (
<>
{
(() => {
const inputs = [];
for (let i = 0; i < 20; i++){
console.log(i);
inputs.push(<input key={i} onChange={(val)=>{onchangeInput(val,i)}} />);
}
return inputs;
})()
}
</>
);
}
const MyCount = () =>{
const myData = [
{
id: 1,
name: 'john',
gender: 'm'
},
{
id: 2,
name: 'mary',
gender: 'f'
}
]
const [count, setCount] = useState(0);
const [datas, setDatas] = useState(myData);
const clkBtn = () =>{
setCount((c) => c + 1);
}
return(
<div>
<button onClick={clkBtn}>+ {count}</button>
{datas.map((data, index) => (
<li key={index}>
<input
type="text"
value={data.name}
onChange={(e) => {
data.name = e.target.value;
setDatas([...datas]);
}}
/>
</li>
))}
</div>
)
}
Base on #Steffan, thus use as your way:
const [arr,arrSet] = useState(array_value);
...
let newArr = [...arr];
arr.map((data,index) => {
newArr[index].somename= new_value;
});
arrSet(newArr);
Use useEffect to check new arr value.
A little late to the party, but it is an option to spread the contents of the array in a new object, then replacing the desired object in the selected index and finally producing an array from that result, it is a short answer, probably not the best for large arrays.
// data = [{name: 'tom', age: 15, etc...}, {name: 'jerry', age: 10, etc...}]
// index = 1
setData(Object.values({...data, [index]: {...data[index], name: 'the mouse' }}))
// data = [{name: 'tom', age: 15, etc...}, {name: 'the mouse', age: 10, etc...}]

React - Sorting Table Columns Inside a Map() Method

I am trying to sort a table (ascending/descending) when my table header data is placed inside a .map() method (see line 73 of the codesandbox link in this post).
I can get the hand emoji to change onClick, but no sorting takes place. I think it may have something to do with passing an object to the map method, and not an individual string or numeric value as found in the codesandbox that I have based this functionality on. Here is the original sandbox that I am modelling after:
https://codesandbox.io/embed/table-sorting-example-ur2z9?fontsize=14&hidenavigation=1&theme=dark
...and here is my codesandbox with my data structure that I need to sort:
https://codesandbox.io/s/table-sorting-example-forked-dofhj?file=/src/App.js
What can I change in order to get each column to be sortable? Any help/advice would be greatly appreciated.
App.js
import React from "react";
import "./styles.css";
import {
Button,
Table,
Thead,
Tbody,
Flex,
Tooltip,
Tr,
Th,
Td
} from "#chakra-ui/react";
//Table Headers
const TABLE_HEADERS = [
{ name: "ID Number" },
{ name: "User Type" },
{ name: "User Category" },
{ name: "User Interest" }
];
const useSortableData = (items, config = null) => {
const [sortConfig, setSortConfig] = React.useState(config);
const sortedItems = React.useMemo(() => {
let sortableItems = [...items];
if (sortConfig !== null) {
sortableItems.sort((a, b) => {
if (a[sortConfig.key] < b[sortConfig.key]) {
return sortConfig.direction === "ascending" ? -1 : 1;
}
if (a[sortConfig.key] > b[sortConfig.key]) {
return sortConfig.direction === "ascending" ? 1 : -1;
}
return 0;
});
}
return sortableItems;
}, [items, sortConfig]);
const requestSort = (key) => {
let direction = "ascending";
if (
sortConfig &&
sortConfig.key === key &&
sortConfig.direction === "ascending"
) {
direction = "descending";
}
setSortConfig({ key, direction });
};
return { items: sortedItems, requestSort, sortConfig };
};
const ProductTable = (props) => {
const { items, requestSort, sortConfig } = useSortableData(
props.myUserErrorTypes
);
const getClassNamesFor = (name) => {
if (!sortConfig) {
return;
}
return sortConfig.key === name ? sortConfig.direction : undefined;
};
return (
<Table>
<caption>User Error Types</caption>
<Thead>
<Tr>
{TABLE_HEADERS.map(({ name, description, isNumeric }) => (
<Th key={name} isNumeric={isNumeric}>
<Button
type="button"
onClick={() => requestSort(name)}
className={getClassNamesFor(name)}
>
<Tooltip label={description} aria-label={description}>
{name}
</Tooltip>
</Button>
</Th>
))}
</Tr>
</Thead>
<Tbody>
{items.map((error) => {
const { userNumber, userType, errorId, errorCategory } = error;
return (
<React.Fragment key={errorId}>
<Tr id={errorId} key={errorId}>
<Td>{userNumber}</Td>
<Td>{userType}</Td>
<Td>{errorId}</Td>
<Td>{errorCategory}</Td>
<Td textAlign="center">
<Flex justify="justifyContent"></Flex>
</Td>
</Tr>
</React.Fragment>
);
})}
</Tbody>
</Table>
);
};
export default function App() {
return (
<div className="App">
<ProductTable
myUserErrorTypes={[
{
userNumber: 1234567890,
userType: "SuperUser",
errorId: 406,
errorCategory: "In-Progress"
},
{
userNumber: 4859687937,
userType: "NewUser",
errorId: 333,
errorCategory: "Complete"
}
]}
/>
</div>
);
}
The items are being sorted by the table header name (e.g. 'ID Number') since you're calling requestSort with the name. Add an id property to the objects in the TABLE_HEADERS array matching the property name (e.g. userNumber) in the data objects and pass it as an argument to both the requestSort and getClassNamesFor functions.
const TABLE_HEADERS = [
{ name: 'ID Number', id: 'userNumber' },
{ name: 'User Type', id: 'userType' },
{ name: 'User Category', id: 'errorId' },
{ name: 'User Interest', id: 'errorCategory' },
]
{
TABLE_HEADERS.map(({ name, id }) => (
<Th key={id}>
<Button
type="button"
onClick={() => requestSort(id)}
className={getClassNamesFor(id)}
>
{name}
</Button>
</Th>
))
}
You're also trying to use the description and isNumeric values from the header object but they are both undefined. You might want to add those properties to the objects in the TABLE_HEADERS array.

How to operate on each individual rendered element when they are mapped from object array in React?

I'm trying to make a shopping app which repeats the same card. Instead of manually rendering them, I used map to render array objects like this:
Parent component:
const Home = () => {
const dummyData = [
{
id: 1,
title: tshirt,
price: 10
},
{
id: 2,
title: hat,
price: 20
}
]
const [totalPrice, setTotalPrice] = useState(0);
const itemNo = 2;
const handleClick = (price) => {
setTotalPrice(price * itemNo);
}
const RenderCards = () => {
return (
dummyData.map(
(d) =>
<Card key={d.id} title={d.title} price={d.price} totalPrice={totalPrice}/>
)
)
}
return(
<>
<RenderCards />
</>
)
}
and the child component:
const Card = (id, title, price, totalPrice) => {
return (
<>
<div key={id}>
<p>{title}</p>
<p>{totalPrice}</p>
<button onClick={() => handleClick(price)}>Click for total price</button>
</div>
</>
)
}
When clicking the button on each card, I'd like to display total price for each card, i.e. for card 1, total price should be 10 * 2 = 20, and for card 2 should be 40. Clicking card 1 should only change {totalPrice} of card 1, card 2 should not be affected, vice versa.
However what I have so far is when clicking the button, both card would show the same total price. I understand this behaviour as the same data is passed to the card component, but how can I individually set data for each card in this case when components are rendered from array map?
const Home = () => {
const dummyData = [
{
id: 1,
title: tshirt,
price: 10
},
{
id: 2,
title: hat,
price: 20
}
]
const [totalPrice, setTotalPrice] = useState([]); //<-- an empty array for start
const handleClick = (id, qty) => {
let newState = [...totalPrice]; //<--- copy the state
if (newState.find(item => item.id === id) != undefined) { // find the item to add qty, if not exists, add one
newState.find(item => item.id === id).qty += qty
} else {
newState.push({id:id, qty:qty});
}
setTotalPrice(newState); //<-- set the new state
}
const RenderCards = () => {
return (
dummyData.map(
(d) => {
const stateItem = totalPrice.find(item=> item.id === d.id); // return the item or undefined
const qty = stateItem ? stateItem.qty : 0 // retreive qty from state by id or 0 if the product is not in the array
return (
<Card key={d.id} title={d.title} price={d.price} totalPrice={d.price * qty}/> //calculate the total
)
}
)
)
}
return(
<>
<RenderCards />
</>
)
}
and card:
const Card = (id, title, price, totalPrice) => {
return (
<>
<div>
<p>{title}</p>
<p>{totalPrice}</p>
<button onClick={() => handleClick(id, 1)}>Click for add one</button> // add one, total price will be good on the next render
</div>
</>
)
}
maybe buggy but the idea is here

How do I update states `onChange` in an array of object in React Hooks

I have retrieved data stored using useState in an array of object, the data was then outputted into form fields. And now I want to be able to update the fields (state) as I type.
I have seen examples on people updating the state for property in array, but never for state in an array of object, so I don't know how to do it. I've got the index of the object passed to the callback function but I didn't know how to update the state using it.
// sample data structure
const data = [
{
id: 1,
name: 'john',
gender: 'm'
}
{
id: 2,
name: 'mary',
gender: 'f'
}
]
const [data, setData] = useState([]);
const updateFieldChanged = index => e => {
console.log('index: ' + index);
console.log('property name: '+ e.target.name);
setData() // ??
}
return (
<React.Fragment>
{data.map((data, index) => {
<li key={data.name}>
<input type="text" name="name" value={data.name} onChange={updateFieldChanged(index)} />
</li>
})}
</React.Fragment>
)
Here is how you do it:
// sample data structure
/* const data = [
{
id: 1,
name: 'john',
gender: 'm'
}
{
id: 2,
name: 'mary',
gender: 'f'
}
] */ // make sure to set the default value in the useState call (I already fixed it)
const [data, setData] = useState([
{
id: 1,
name: 'john',
gender: 'm'
}
{
id: 2,
name: 'mary',
gender: 'f'
}
]);
const updateFieldChanged = index => e => {
console.log('index: ' + index);
console.log('property name: '+ e.target.name);
let newArr = [...data]; // copying the old datas array
// a deep copy is not needed as we are overriding the whole object below, and not setting a property of it. this does not mutate the state.
newArr[index] = e.target.value; // replace e.target.value with whatever you want to change it to
setData(newArr);
}
return (
<React.Fragment>
{data.map((datum, index) => {
<li key={datum.name}>
<input type="text" name="name" value={datum.name} onChange={updateFieldChanged(index)} />
</li>
})}
</React.Fragment>
)
The accepted answer leads the developer into significant risk that they will mutate the source sequence, as witnessed in comments:
let newArr = [...data];
// oops! newArr[index] is in both newArr and data
// this might cause nasty bugs in React.
newArr[index][propertyName] = e.target.value;
This will mean that, in some cases, React does not pick up and render the changes.
The idiomatic way of doing this is by mapping your old array into a new one, swapping what you want to change for an updated item along the way.
setData(
data.map(item =>
item.id === index
? {...item, someProp : "changed", someOtherProp: 42}
: item
))
setDatas(datas=>({
...datas,
[index]: e.target.value
}))
with index being the target position and e.target.value the new value
You don't even need to be using the index ( except for the key if you want ) nor copying the old datas array,and can even do it inline or just pass data as an argument if you prefer updateFieldChanged to not be inline. It's done very quickly that way :
const initial_data = [
{
id: 1,
name: "john",
gender: "m",
},
{
id: 2,
name: "mary",
gender: "f",
},
];
const [datas, setDatas] = useState(initial_data);
return (
<div>
{datas.map((data, index) => (
<li key={index}>
<input
type="text"
value={data.name}
onChange={(e) => {
data.name = e.target.value;
setDatas([...datas]);
}}
/>
</li>
))}
</div>
);
};
This is what I do:
const [datas, setDatas] = useState([
{
id: 1,
name: "john",
gender: "m",
},
{
id: 2,
name: "mary",
gender: "f",
},
]);
const updateFieldChanged = (name, index) => (event) => {
let newArr = datas.map((item, i) => {
if (index == i) {
return { ...item, [name]: event.target.value };
} else {
return item;
}
});
setDatas(newArr);
};
return (
<React.Fragment>
{datas.map((data, index) => {
<li key={data.name}>
<input
type="text"
name="name"
value={data.name}
onChange={updateFieldChanged("name", index)}
/>
</li>;
<li key={data.gender}>
<input
type="text"
name="gender"
value={data.gender}
onChange={updateFieldChanged("gender", index)}
/>
</li>;
})}
</React.Fragment>
);
Spread the array before that. As you cannot update the hook directly without using the method returned by useState
const newState = [...originalState]
newState[index] = newValue
setOriginalState(newState)
This will modify the value of the state and update the useState hook if its an array of string.
const updateFieldChanged = index => e => {
name=e.target.name //key
let newArr = [...data]; // copying the old datas array
newArr[index][name] = e.target.value; //key and value
setData(newArr);
}
return (
<React.Fragment>
{data.map((datum, index) => {
<li key={datum.name}>
<input type="text" name="name" value={datum.name} onChange={updateFieldChanged(index)} />
</li>
})}
</React.Fragment>
)
const [datas, setDatas] = useState([ { id: 1, name: 'john', gender: 'm' } { id: 2, name: 'mary', gender: 'f' } ]);
const updateFieldChanged = (index, e) => { const updateData = { ...data[index], name: e.target.name }
setData([...data.slice(0, index), updateData, ...data.slice(index + 1)]) }
I am late to reply but I had also same problem, so I got solution through this query. Have a look on it if it can help you.The example that I did is that I have a state , named OrderList, and so a setter for it is SetOrderList. To update a specific record in a list, am using SetOrderList and passing it a map function with that list of which I need to change, so I will compare Index or Id of my list, where it will match, I will change that specific record.
const Action = (Id, Status) => { //`enter code here`Call this function onChange or onClick event
setorderList([...orderList.map((order) =>
order.id === Id ? { ...order, status: Status } : order
),
]);
}
complete example for update value based on index and generate input based on for loop....
import React, { useState,useEffect } from "react";
export default function App() {
const [datas, setDatas] =useState([])
useEffect(() => {
console.log("datas");
console.log(datas);
}, [datas]);
const onchangeInput = (val, index) =>{
setDatas(datas=>({
...datas,
[index]: val.target.value
}))
console.log(datas);
}
return (
<>
{
(() => {
const inputs = [];
for (let i = 0; i < 20; i++){
console.log(i);
inputs.push(<input key={i} onChange={(val)=>{onchangeInput(val,i)}} />);
}
return inputs;
})()
}
</>
);
}
const MyCount = () =>{
const myData = [
{
id: 1,
name: 'john',
gender: 'm'
},
{
id: 2,
name: 'mary',
gender: 'f'
}
]
const [count, setCount] = useState(0);
const [datas, setDatas] = useState(myData);
const clkBtn = () =>{
setCount((c) => c + 1);
}
return(
<div>
<button onClick={clkBtn}>+ {count}</button>
{datas.map((data, index) => (
<li key={index}>
<input
type="text"
value={data.name}
onChange={(e) => {
data.name = e.target.value;
setDatas([...datas]);
}}
/>
</li>
))}
</div>
)
}
Base on #Steffan, thus use as your way:
const [arr,arrSet] = useState(array_value);
...
let newArr = [...arr];
arr.map((data,index) => {
newArr[index].somename= new_value;
});
arrSet(newArr);
Use useEffect to check new arr value.
A little late to the party, but it is an option to spread the contents of the array in a new object, then replacing the desired object in the selected index and finally producing an array from that result, it is a short answer, probably not the best for large arrays.
// data = [{name: 'tom', age: 15, etc...}, {name: 'jerry', age: 10, etc...}]
// index = 1
setData(Object.values({...data, [index]: {...data[index], name: 'the mouse' }}))
// data = [{name: 'tom', age: 15, etc...}, {name: 'the mouse', age: 10, etc...}]

How to remove a specific component in React? (Using key and id)

I was going through the React docs and was attempting to modify a TodoList Item.
https://codesandbox.io/s/43njy6458x
I am trying to remove each component with a button, however the button does not delete the item.
I have tried a few methods to filter the list, yet none have successfully accomplished the function.
const ToDo = props => (
<tr>
<td>
<label>{props.id}</label>
</td>
<td>
<input />
</td>
<td>
<label>{props.createdAt.toTimeString()}</label>
<button
type="button"
onClick={props.deleteItem}
value={props.id}
>
Delete
</button>
</td>
</tr>
);
class ToDoList extends React.Component {
constructor() {
super();
const date = new Date();
const toDoCounter = 1;
this.state = {
list: [
{
id: toDoCounter,
createdAt: date,
},
],
toDoCounter: toDoCounter,
};
}
sortByEarliest() {
const sortedList = this.state.list.sort((a, b) => {
return a.createdAt - b.createdAt;
});
this.setState({
list: [...sortedList],
});
}
sortByLatest() {
const sortedList = this.state.list.sort((a, b) => {
return b.createdAt - a.createdAt;
});
this.setState({
list: [...sortedList],
});
}
addToEnd() {
const date = new Date();
const nextId = this.state.toDoCounter + 1;
const newList = [
...this.state.list,
{id: nextId, createdAt: date},
];
this.setState({
list: newList,
toDoCounter: nextId,
});
}
addToStart() {
const date = new Date();
const nextId = this.state.toDoCounter + 1;
const newList = [
{id: nextId, createdAt: date},
...this.state.list,
];
this.setState({
list: newList,
toDoCounter: nextId,
});
}
// this is the issue
deleteItem(event) {
const clickedId = event.target.value;
//console.log(clickedId);
const arrDes = [...this.state.list];
const newList = this.state.list.filter((item) => {
//console.log(item.id);
return item.id !== clickedId;
})
this.setState({
list: newList
});
}
render() {
return (
<div>
<code>key=id index=id</code>
<br />
<button onClick={this.addToStart.bind(this)}>
Add New to Start
</button>
<button onClick={this.addToEnd.bind(this)}>
Add New to End
</button>
<button onClick={this.sortByEarliest.bind(this)}>
Sort by Earliest
</button>
<button onClick={this.sortByLatest.bind(this)}>
Sort by Latest
</button>
<table>
<tr>
<th>ID</th>
<th />
<th>created at</th>
</tr>
{this.state.list.map((todo, index) => (
<ToDo key={todo.id} value={todo.id} deleteItem={this.deleteItem.bind(this)} {...todo} />
))}
</table>
</div>
);
}
}
Isolated code in question:
// this is the issue
deleteItem(event) {
const clickedId = event.target.value;
//console.log(clickedId);
const arrDes = [...this.state.list];
const newList = this.state.list.filter((item) => {
//console.log(item.id);
return item.id !== clickedId;
})
this.setState({
list: newList
});
}
You need to cast clickedId to a natural number to match the id of the list element:
const newList = this.state.list.filter(item => {
return item.id !== +clickedId
});
The + operator is one way to do that.
Your function can be simplified:
deleteItem(event) {
// Grab the value from the clicked button by
// using destructuring to pick out that property
const { value } = event.target;
// return an array that doesn't include objects with ids
// that match the value - coerced to a number from a string
// so it matches the id type in your data
const newList = this.state.list.filter(item => item.id !== Number(value));
this.setState({ list: newList });
}

Categories