Deleting component elements from an array in reactjs - reactjs

I'm fairly new to react. What I'm trying to do is to delete a component from an array given an index when the "[x]" button is clicked.
From what I understood react re-renders the Component whenever the state has changed.
The labels variable on the render() method is an array of components and also has other child components. When the user clicks the [x] on one of the components, it would pass the index of that to the class function closeLabelHandler() and assign the state objects, indexToDelete and isDeletingLabel to the index of the element and true, respectively.
Since I am changing a state object, I was thinking that this during the re-render an element would be deleted from the labels array but alas, it doesn't.
Here's my code:
import React, { Component } from 'react';
import "./ImageContainer.css";
import { Stage, Layer, Image, Tag, Text, Label } from 'react-konva';
import ToolsContainer from '../ToolsContainer/ToolsContainer';
class ImageContainer extends Component {
state = {
showImageContainer: true,
canvasHeight: 0,
canvasWidth: 0,
canvasImageUrl: "",
image: null,
commentsArray: [],
labelCount: 0,
isDeletingLabel: false,
indexToDelete: -1
}
componentDidMount() {
let stage = this.refs.stage
const imgObj = new window.Image();
imgObj.src = this.props.selectedImageURI
imgObj.onload = () => {
this.setState({ image: imgObj })
this.setState({ canvasWidth: imgObj.width, canvasHeight: imgObj.height });
let canvasImageUrl = stage.toDataURL();
this.setState({ canvasImageUrl: canvasImageUrl })
}
}
getLabelCount = (count) => {
this.setState({ labelCount: count })
}
displayContainerHandler = () => {
this.props.showImageContainer(!this.state.showImageContainer)
}
downloadImageHandler = () => {
let a = document.createElement('a');
a.href = this.state.canvasImageUrl.replace("image/png", "image/octet-stream");
a.download = 'shot.png';
a.click();
}
closeLabelHandler = (index) => {
this.setState({
isDeletingLabel: true,
indexToDelete: index
})
console.log("[closeLabelHandler] " + index)
}
render() {
console.log("[ImageContainer][render]" + this.state.commentsArray)
let labels = [];
for (let i = 0; i < this.state.labelCount; i++) {
labels.push(
<Label key={i} draggable={true} x={150 + i * 2} y={150 + i * 2} >
<Tag
fill="black"
pointerWidth={10}
pointerHeight={10}
lineJoin='round'
shadowColor='black'
/>
<Text
text="Insert Comment Here"
fontFamily='Calibri'
fontSize={18}
padding={5}
fill='white'
/>
<Text
text="[x]"
fontFamily='Calibri'
fontSize={18}
x={170}
fill='black'
onClick={() => this.closeLabelHandler(i)}
/>
</Label>
)
if (this.state.isDeletingLabel) {
labels.splice(this.state.indexToDelete, 1)
this.setState({
isDeletingLabel: false,
indexToDelete: -1
})
}
}
return (
<div className="ImageContainer" >
<button className="ImageContainer-close-button " onClick={this.displayContainerHandler}>[x]</button>
<Stage ref="stage" height={this.state.canvasHeight * .5} width={this.state.canvasWidth * .5} >
<Layer>
<Image image={this.state.image} scaleX={0.5} scaleY={0.5} />
{labels}
</Layer>
</Stage>
<ToolsContainer getLabelCount={count => this.getLabelCount(count)} />
<button className="pure-button" onClick={() => this.downloadImageHandler()}>Download</button>
</div>
)
}
}
Any help would be appreciated.

This is a simple way to achive what you want. You should not change your state directly, it could cause inconsistency to your application.
import React, { Component } from "react";
class Books extends Component {
state = {
books: [{ id: 1, title: "Book 1" }, { id: 2, title: "Book 2" }]
};
handleDelete = book => {
const books = this.state.books.filter(b => b.id !== book.id);
this.setState({ books });
};
render() {
return (
<ul>
{this.state.books.map(book => (
<li key={book.id}>
{book.title}{" "}
<button onClick={() => this.handleDelete(book)}>x</button>
</li>
))}
</ul>
);
}
}
export default Books;

Related

React class component button setState sorting not working as intended

import AuthorSidebar from "../SubPages/AuthorSidebar";
import ReactPaginate from 'react-paginate';
import { Card, Button } from 'react-bootstrap';
export default class Author extends React.Component {
constructor(props) {
super(props);
this.state = {
author: [],
AuthorTempState: [],
selectedPage: 0,
Postsperpage: 4,
PagesVisited: 0
}
this.handlePageClick = this.handlePageClick.bind(this);
}
async recievedData() {
const res = await fetch(`https://api.quotable.io/authors?limit=30`);
const data = await res.json();
for (const element of data.results) {
element.idfav = false;
}
data.results.sort((a, b) => (a._id > b._id) ? 1 : -1)
this.setState({
author: data.results,
AuthorTempState: data.results
});
}
componentDidMount() {
if (localStorage.getItem('authors')) {
this.setState({
author: JSON.parse(localStorage.getItem('authors')),
AuthorTempState: JSON.parse(localStorage.getItem('authors'))
})
} else {
this.recievedData();
}
}
componentDidUpdate(prevProps, prevState) {
if (this.state.author !== prevState.author) {
localStorage.setItem('authors', JSON.stringify(this.state.author))
}
}
favBttn(Auth) {
const filterData = this.state.AuthorTempState.filter(data => data._id !== Auth._id)
Auth.idfav = true;
const updateAuthor = [Auth, ...filterData];
updateAuthor.sort((a, b) => (a._id > b._id) ? 1 : -1)
this.setState({
author: updateAuthor
});
}
remfavBttn(Auth) {
const filterData = this.state.AuthorTempState.filter(data => data._id !== Auth._id)
Auth.idfav = false;
const updateAuthor = [Auth, ...filterData]
updateAuthor.sort((a, b) => (a._id > b._id) ? 1 : -1)
this.setState({
author: updateAuthor
});
}
handlePageClick = (e) => {
const SelectedPage = e.selected;
const Offset = SelectedPage * this.state.Postsperpage;
this.setState({
selectedPage: SelectedPage,
PagesVisited: Offset
}, () => {
this.recievedData();
});
};
render() {
const { author } = this.state;
const PageCount = Math.ceil(author.length / this.state.Postsperpage);
console.log(author)
let sliced = author.slice(this.state.PagesVisited, this.state.PagesVisited + this.state.Postsperpage);
return (
<div className="AppWhole">
<AuthorSidebar />
<div className="App">
<div className="author">
{sliced.map(
(Author) => (
<div key={Author._id}>
<Card style={{ margin: 20 }} border="dark" bg="light" text="grey">
<Card.Body>
<Card.Title>Name: {Author.name}
{
(Author.idfav) ? (<Button size="sm" className='right' onClick={() =>
this.remfavBttn(Author)
}>Remove Favt.</Button >) : (<Button size="sm" className='right' onClick={() =>
this.favBttn(Author)
}>Add Favt.</Button >)
}
</Card.Title>
<Card.Text>
Bio: {Author.bio}
</Card.Text>
</Card.Body>
<Card.Footer>Wiki: <a href='{Author.link}'>{Author.link}</a></Card.Footer>
</Card>
</div>
))}
<div >
<ReactPaginate
pageCount={PageCount}
onPageChange={this.handlePageClick}
previousLabel={"<<"}
nextLabel={">>"}
containerClassName={'paginationLinks'}
disabledClassName={'paginationDisabled'}
activeClassName={'paginationActive'}
/>
</div>
</div>
</div>
</div>
);
}
}
So my page is an Author page which shows different authors and their details in each card which I fetched from API and then mapped. https://i.stack.imgur.com/QitTe.png
And in each card after onclick it changes to Remove Favourite. The card which is favourited makes the idfav true in the object array of the author state and false if not favourited. And there is a 2nd page which shows all the favourite authors. Now after clicking once on a card to remove fav and then clicking another card also to remove favourite the former card gets turned to add favourite automatically.
Please help me I have been stuck on this for 2 weeks now. Thank you.
Since you need to update a single object in-place in a list, here's how you do that really simply.
const bttn = (idfav) => Auth => this.setState({
author: this.state.author.map(a =>
a._id === Auth._id
// When we find the auth to update, change its idfav
? {...a, idfav }
: a
});
const favBttn = bttn(true);
const remfavBttn = bttn(false);
Or if you prefer the undried version:
function async favBttn(Auth) {
this.setState({
author: this.state.author.map(a =>
a._id === Auth._id
// When we find the auth to update, change its idfav
? {...a, idfav: true }
: a
});
}
function async favBttn(Auth) {
this.setState({
author: this.state.author.map(a =>
a._id === Auth._id
// When we find the auth to update, change its idfav
? {...a, idfav: false }
: a
});
}

Counters for each item in React.js

I know that some similar issues already have been posted here, but I cannot find the solution for my issue and here I am stuck with this code.
The problem is when I want to Increase or Decrease a counter, all other counters changes. Also I'm wondering were should I store the counter, should it be a unique variable or an array for each item.. ?
I'm having:
product_items.jsx
import React, { Component } from "react";
import Item from "./product_item";
class Items extends Component {
constructor(props) {
super(props);
this.state.unique = 0;
}
state = {
brands: [
{
id: 0,
name: "Water0",
price: "1.53",
{
id: 1,
name: "Water1",
price: "1.47",
},
{
id: 2,
name: "Water2",
price: "1.58",
},
{
id: 3,
name: "Water3",
price: "1.41",
category: "Still Water",
},
],
counter: 10,
counters: [
{ id: 1, value: 0 },
],
filterBrands: [],
};
componentDidMount() {
this.setState({
filterBrands: this.state.brands,
});
}
handleClick = (name) => {
let filterBrands = [];
if (name === "All") {
filterBrands = this.state.brands;
} else {
filterBrands = this.state.brands.filter(
(brands) => brands.category === name
);
}
this.setState({ filterBrands });
};
addToCart = (counter) => {
console.log("addToCart DONE");
this.setState(({ unique }) => ({
unique: unique + 1,
}));
};
handleIncrement = (counter) => {
// console.log(counter);
console.log("handleIncrement DONE");
const counters = [...this.state.counters];
const index = counters.indexOf(counter);
counters[index] = { ...counter };
counters[index].value++;
// this.setState({ counters });
// this.setState({ unique: this.state.unique + 1 });
// console.log(this.state.unique);
this.setState(({ unique }) => ({
unique: this.state.unique + 1,
}));
};
handleDecrease = (counter) => {
console.log("handleDecrease DONE");
const counters = [...this.state.counters];
const index = counters.indexOf(counter);
counters[index] = { ...counter };
counters[index].value--;
// this.setState({ counters });
// this.setState({ unique: this.state.unique - 1 });
this.setState(({ unique }) => ({
unique: this.state.unique - 1,
}));
};
render() {
return (
<div className="ctg-flex">
<div className="items">
{this.state.filterBrands.map((id, brands) => (
<Item
// key={id.key}
key={brands}
dataText={id.name}
dataPrice={id.price}
// counter_unique={this.state.counter_unique}
unique={this.state.unique}
// counter={this.state.counters.value}
counters={this.state.counters}
onClickaddToCart={this.addToCart}
onIncrement={this.handleIncrement}
onDecrease={this.handleDecrease}
/>
))}
</div>
</div>
);
}
}
export default Items;
product_item.jsx
import React, { Component } from "react";
import Card from "react-bootstrap/Card";
import Button from "react-bootstrap/Button";
import Counter from "./counter";
class Item extends Component {
render() {
return (
<Card >
<Card.Body>
<Card.Text>
this.props.unique : {this.props.unique}
// this.props.counters.value : {this.props.counters.value} //test isn't working
</Card.Text>
</Card.Body>
<Counter
// counters={counters}
counter={this.props.counter}
onIncrement={this.props.onIncrement}
onDecrease={this.props.onDecrease}
unique={this.props.unique}
/>
<Button
style={{
display: this.props.unique === 0 ? "block" : "none",
}}
onClick={() => this.props.onClickaddToCart(this.props.counter)}
>
ADD TO CART
</Button>
</Card>
);
}
}
export default Item;
and
counter.jsx
import React, { Component } from "react";
import Button from "react-bootstrap/Button";
class Counter extends Component {
render() {
return (
<div
style={{
display: this.props.unique === 0 ? "none" : "flex",
}}
>
<Button
onClick={() => this.props.onDecrease(this.props.counter)}
>
Decrease
</Button>
<span>
{this.props.unique} in cart
</span>
<Button
onClick={() => this.props.onIncrement(this.props.counter)}
>
Increase
</Button>
</div>
);
}
}
export default Counter;
Your counter should be an object whose key will be the id values of your products .
counter = {
[id1] : 0 , //initially 0
[id2] : 0
... and so on
}
then each time you increase the count get the id of the product and increase the counter
handleIncrement = (counter) => {
// get the id of the clicked product
const newCounter = { ... this.state.counter , [id] : counter[id]+1}
this.setState({counter : newCounter})
};
and get the counter value for each product object
this.state.counter[productId]
so final I get it, I merged product and counter in a single array to catch same single key in all array and use count properly.
product_items.jsx
state = {
brands: [
{
id: 0,
name: "Water0",
price: "1.53",
value: 0,
},
{
id: 1,
name: "Water1",
price: "1.17",
value: 0,
},
...
]
}
...
render() {
return (
<div >
{this.state.brands.map((item, key) => (
<Item
key={key}
item={item}
id={key}
onClickaddToCart={this.addToCart}
onIncrement={this.handleIncrement}
onDecrease={this.handleDecrease}
brands={this.state.brands}
/>
))}
</div>
);
}

Delete an element by key from Array

I used library react-sortable-hoc for drag and drop element, but the library documentation does not have any actions for delete items. I want to delete, drag and drop item when click on close button. Which method is right for removing elements by key from object?
React
const SortableItem = SortableElement(({ value }: { value: string }, onRemove: any) =>
<div className="dragItems" style={{ background: 'gray' }}>
<img src={value} alt="" />
<button className="dragCloseBtn" onClick={() => onRemove(any)} />
</div>
);
const SortableList = SortableContainer(({ items }: { items: string[] }) => {
return (
<div className="dragAndDrop">
{items.map((value, index) => (
<SortableItem key={'item-${index}'} index={index} value={value} />
))}
</div>
);
});
constructor(props: any) {
super(props);
this.state = {
items: [
{
"id": 0,
"link": "https://via.placeholder.com/150"
},
{
"id": 1,
"link": "https://via.placeholder.com/150"
}
],
};
}
public onSortEnd = ({ oldIndex, newIndex }: { oldIndex: number, newIndex: number }) => {
this.setState({
items: arrayMove(this.state.items, oldIndex, newIndex),
});
};
public onRemove(e: { target: { value: any; }; }) {
const array = [...this.state.items];
const index = array.indexOf(e.target.value)
if (index !== -1) {
array.splice(index, 1);
this.setState({items: array});
}
}
<SortableList items={this.state.items}
onSortEnd={this.onSortEnd}
lockAxis="xy"
axis="xy" />
UPDATED:
Hi there, I figured out what went wrong and made a successful remove event on your application.
Everything is illustrated with comments at this codesandbox.
=========
I modified this one, it should do the required using Array's filter method.
public onRemove(e: { target: { value: any; }; }) {
let array = [...this.state.items];
const intId = parseInt(e.target.value, 10);
array = array.filter(item => item.id !== intId);
this.setState({items: array});
}
So there were few problems in your code! You seemed to be confuse how react works with passing down props. You have to pass down the method required for remove. And you should bind it inside the class that you will be calling it.
onRemove should be bound to current context
onRemove should be passed down across the component tree
Check my //[NOTE]====> comments for additional explanation
Working code sandbox is here
import * as React from "react";
import * as ReactDOM from "react-dom";
import {
arrayMove,
SortableContainer,
SortableElement
} from "react-sortable-hoc";
//[NOTE]====>value contains the relevent object containing the callback. Onclick call it with the relevant id
const SortableItem = SortableElement(
({ value }: { value: any }, onRemove: any) => (
<div className="dragItems" style={{ background: "gray", margin: "20px" }}>
<img src={value.link} alt="" />
<button className="dragCloseBtn" onClick={() => value.onRemove(value.id)}>
{" "}
X{" "}
</button>
</div>
)
);
const SortableList = SortableContainer(({ items }: { items: string[] }) => {
return (
<div className="dragAndDrop">
{items.map((value, index) => (
<SortableItem key={`item-${index}`} index={index} value={value} />
))}
</div>
);
});
class SortableComponent extends React.Component<{}, { items: string[] }> {
constructor(props: {}) {
super(props);
//[NOTE]====>Send the callback on each element bound to the current context
//This is like telling the function from where exactly the function will be called
this.state = {
items: [
{
id: 0,
link: "https://via.placeholder.com/150",
onRemove: this.onRemove.bind(this)
},
{
id: 1,
link: "https://via.placeholder.com/150",
onRemove: this.onRemove.bind(this)
}
]
};
}
public render() {
return <SortableList items={this.state.items} onSortEnd={this.onSortEnd} />;
}
public onSortEnd = ({
oldIndex,
newIndex
}: {
oldIndex: number;
newIndex: number;
}) => {
this.setState({
items: arrayMove(this.state.items, oldIndex, newIndex)
});
};
//[NOTE]====>Use the id to filter out and set the new set of items
public onRemove(id) {
console.log(id);
let array = [...this.state.items];
const intId = parseInt(id, 10);
array = array.filter((item: any) => item.id !== intId);
this.setState({ items: array });
}
}
ReactDOM.render(<SortableComponent />, document.getElementById("root"));

How to collect Switch value in React JS + Ant Design

I'm sorry for my basic knowledge and my basic english :)
My Question is: How to concenate switch value [I use Switch for each record in table]
This is the table
https://ibb.co/3TVLBK6
I've created table with one cell/field using Switch (On/Off)
And i want get the switch value when they are on
And here is the code
const columnsTableDepartmentModal = [
{
title: 'No',
dataIndex: 'no',
key: 'no',
},
{
title: 'Department',
dataIndex: 'department',
key: 'department',
},
{
title: 'Select',
dataIndex: 'select_department',
key: 'select_department',
render: (e, record) => (
<Switch
defaultChecked={e}
onChange={
(value) => onChangeSwitch(value,record)
}
checkedChildren="Yes"
unCheckedChildren="No"
/>
),
}];
This is what i now try
function onChangeSwitch(isSelect,record){
console.log(e); // True / False
console.log(record); // True / False
if(isSelect){
// push data to array
}
if(!isSelect){
// pop data from array
}
}
This is how i show the table
<Modal
title={modalDepartmentTitle}
visible={visibleDepartment}
width={800}
onOk={handleOkDepartment}
onCancel={handleCancelDepartment}
footer={[
<Button key="submit" type="primary" onClick={handleOkDepartment}>OK</Button>,
<Button key="button" type="danger" onClick={handleDeleteDepartment}>DELETE</Button>,
<Button key="back" onClick={handleCancelDepartment}>CANCEL</Button>,
]}
>
<Table
columns={columnsTableDepartmentModal}
dataSource={stateDepartment.data}
pagination={false}
scroll={{y: 325}}
/>
</Modal>
Expected result: 1,3,4
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
import Switch from 'react-switch';
class App extends Component {
constructor() {
super();
this.state = {
checkedPos: [],
info: [],
};
}
componentWillMount()
{
let tmp = []
let pos = []
for (var i = 1; i < 6; i++) {
tmp.push("Info " + i)
pos.push(false);
}
this.setState({
info: tmp,
checkedPos: pos
})
}
handleChange(index, info)
{
if (!this.state.checkedPos[index])
{
this.setState(prev => ({
checkedPos: prev.checkedPos.map((val, i) => !val && i === index ? true : val),
}))
}
else if (this.state.checkedPos[index])
{
this.setState( prev => ({
checkedPos: prev.checkedPos.map((val, i) => val && i === index ? false : val),
}))
}
}
render() {
const listItems = this.state.info.map((item, index) =>
<div>
{item}
<Switch checked={this.state.checkedPos[index]}
onChange={ () => this.handleChange(index, this.state.info[index])}/>
{" Value is " + this.state.checkedPos[index]}
</div>
);
return (
<div>
{listItems}
</div>
);
}
}
render(<App />, document.getElementById('root'));
Demo here
Acording to #jose answer i've implemented to hook concept with this code
const [theArray, setTheArray] = useState([]);
const addEntry = useCallback((value) => {
setTheArray([...theArray, `${value}`]);
});
Then in function we can add value
function onChangeSwitch(isSelect,record){
console.log(isSelect); // True / False
console.log(record);
addEntry(record.no);
if(isSelect){
// push data to array
}
if(!isSelect){
// pop data from array
}
}
So when we display {enty}
<div key="adkfkdfkda">{theArray.map(entry =>
<span key={entry}>{entry},</span>
)}
</div>
We got value merged in string format
https://ibb.co/nQFK1C4
Thanks.

How to update state in map function in reactjs

I am having 4 buttons each button have name id and selected boolean flag.
What I am trying to achieve is, on click of button, boolean button flag should be changed of that particular button. For this, I need to setState in map function for that particular button Id.
My issue is I am unable to setState in map function for that particular clicked button, its btnSelected should be changed
My aim is to create a multi-select deselect button.Its kind of interest selection for the user and based on that reflect the UI as well my array. Here is my code.
Thanks in anticipation.
import React, { Component } from "react";
import { Redirect } from "react-router-dom";
export default class Test extends Component {
constructor(props, context) {
super(props, context);
this.handleChange = this.handleChange.bind(this);
this.state = {
value: "",
numbers: [1, 2, 3, 4, 5],
posts: [
{
id: 1,
topic: "Animal",
btnSelected: false
},
{
id: 2,
topic: "Food",
btnSelected: false
},
{
id: 3,
topic: "Planet",
btnSelected: false
},
{ id: 4, topic: "Nature", btnSelected: false }
],
allInterest: []
};
}
handleChange(e) {
//console.log(e.target.value);
const name = e.target.name;
const value = e.target.value;
this.setState({ [name]: value });
}
getInterest(id) {
this.state.posts.map(post => {
if (id === post.id) {
//How to setState of post only btnSelected should change
}
});
console.log(this.state.allInterest);
if (this.state.allInterest.length > 0) {
console.log("Yes we exits");
} else {
console.log(id);
this.setState(
{
allInterest: this.state.allInterest.concat(id)
},
function() {
console.log(this.state);
}
);
}
}
render() {
return (
<div>
{this.state.posts.map((posts, index) => (
<li
key={"tab" + index}
class="btn btn-default"
onClick={() => this.getInterest(posts.id)}
>
{posts.topic}
<Glyphicon
glyph={posts.btnSelected === true ? "ok-sign" : "remove-circle"}
/>
</li>
))}
</div>
);
}
}
Here's how you do something like this:
class App extends Component {
state = {
posts: [{
name: 'cat',
selected: false,
}, {
name: 'dog',
selected: false
}]
}
handleClick = (e) => {
const { posts } = this.state;
const { id } = e.target;
posts[id].selected = !this.state.posts[id].selected
this.setState({ posts })
}
render() {
return (
<div>
<form>
{this.state.posts.map((p, i) => {
return (
<div>
<label>{p.name}</label>
<input type="radio" id={i} key={i} checked={p.selected} onClick={this.handleClick} />
</div>
)
})}
</form>
</div>
);
}
}
render(<App />, document.getElementById('root'));
Working example here.
You can do this by passing the index from the map into each button's handleClick function, which would then return another function that can be triggered by an onClick event.
In contrast to Colin Ricardo's answer, this approach avoids adding an id prop onto each child of the map function that is only used for determining the index in the handleClick. I've modified Colin's example here to show the comparison. Notice the event parameter is no longer necessary.
class App extends Component {
state = {
posts: [{
name: 'cat',
selected: false,
}, {
name: 'dog',
selected: false
}]
}
handleClick = (index) => () => {
const { posts } = this.state;
posts[index].selected = !this.state.posts[index].selected
this.setState({ posts })
}
render() {
return (
<div>
<form>
{this.state.posts.map((p, i) => {
return (
<div>
<label>{p.name}</label>
<input type="checkbox" key={i} checked={p.selected} onClick={this.handleClick(i)} />
</div>
)
})}
</form>
</div>
);
}
}
render(<App />, document.getElementById('root'));
Working example here

Resources