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

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 });
}

Related

advice with removing object from react state array

So this has me puzzled. I've been banging my head against the wall trying to figure this out.
So I am trying to remove an object from a state managed array. I don't believe I am mutating the array.
I am using prevState. My delete function which gets sent to another component
{this.state.educationArray.map((item, i) => (<RenderEducation education={item} onDelete={this.handleDelete} />))}
Sending back the id to the handleDelete function.
My handleDelete:
handleDelete = itemId => {
//const newStudy = this.state.educationArray.filter(item => { return item.id !== itemId });
//this.setState({ educationArray: newStudy })
let tempArray = [];
let num = this.state.educationArray.length;
for (let i = 0; i < num;) {
//console.log("itemId is: ", itemId)
let tempId = this.state.educationArray[i].id
if (tempId != itemId) {
let obj = this.state.educationArray[i]
tempArray.push(obj)
}
i++
}
this.setState(prevState => ({ educationArray: tempArray }));
}
Stack Snippet w/loop:
const { useState } = React;
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
educationArray: [
{ id: 1, name: "One" },
{ id: 2, name: "Two" },
{ id: 3, name: "Three" },
],
};
}
handleDelete = (itemId) => {
// const newStudy = this.state.educationArray.filter(item => { return item.id !== itemId });
// this.setState({ educationArray: newStudy })
let tempArray = [];
let num = this.state.educationArray.length;
for (let i = 0; i < num; ) {
//console.log("itemId is: ", itemId)
let tempId = this.state.educationArray[i].id;
if (tempId != itemId) {
let obj = this.state.educationArray[i];
tempArray.push(obj);
}
i++;
}
this.setState((prevState) => ({ educationArray: tempArray }));
};
render() {
return (
<ul>
{this.state.educationArray.map((element) => (
<li key={element.id}>
{element.name}{" "}
<input type="button" value="Del" onClick={() => this.handleDelete(element.id)} />
</li>
))}
</ul>
);
}
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Example />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
Stack Snippet w/filter:
const { useState } = React;
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
educationArray: [
{ id: 1, name: "One" },
{ id: 2, name: "Two" },
{ id: 3, name: "Three" },
],
};
}
handleDelete = (itemId) => {
const newStudy = this.state.educationArray.filter(item => { return item.id !== itemId });
this.setState({ educationArray: newStudy })
/*
let tempArray = [];
let num = this.state.educationArray.length;
for (let i = 0; i < num; ) {
//console.log("itemId is: ", itemId)
let tempId = this.state.educationArray[i].id;
if (tempId != itemId) {
let obj = this.state.educationArray[i];
tempArray.push(obj);
}
i++;
}
this.setState((prevState) => ({ educationArray: tempArray }));
*/
};
render() {
return (
<ul>
{this.state.educationArray.map((element) => (
<li key={element.id}>
{element.name}{" "}
<input type="button" value="Del" onClick={() => this.handleDelete(element.id)} />
</li>
))}
</ul>
);
}
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Example />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
I've tried using the 2 lines commented out, I've tried rearranging how I do the for loop, its always the same result, it never removes the intended id.
I have sent console.log after console.log of the ids getting moved around and every seems to be working, but when it comes right now to push the specific objects that don't match the id to the temp array it never works and the object add the end gets removed.
Please and thank you for your advice
EDIT:
i call the handleDelete inside RenderEducation component:
<button onClick={() => this.props.onDelete(this.state.id)}> X - {this.state.id}</button>
from each
and my constructor:
constructor(props) {
super(props);
this.state = {
educationArray: [],
}
}
and how i add to the array:
addEducation = (e) => {
e.preventDefault();
this.setState(prevState => ({
educationArray: [...prevState.educationArray, {
id: uniqid(),
school: '',
study: '',
dateFrom: '',
dateTo: '',
editing: true,
}]
}))
}
Both versions of your code work in regular, non-edge-case situations, as we can see from the Stack Snippets I added to your question. The only problem I can see with the code shown is that it's using a potentially out-of-date version of the educationArray. Whenever you're updating state based on existing state, it's best to use the callback form of the state setter and use the up-to-date state information provided to the callback. Both of your versions (even your loop version, which does use the callback) are using this.state.educationArray instead, which could be out of date.
Instead, use the array in the state passed to the callback:
handleDelete = (itemId) => {
// Work with up-to-date state via the callback
this.setState(({educationArray: prevArray}) => {
// Filter out the element you're removing
return {
educationArray: prevArray.filter(({id}) => id !== itemId)
};
});
};
Live Example:
const { useState } = React;
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
educationArray: [
{ id: 1, name: "One" },
{ id: 2, name: "Two" },
{ id: 3, name: "Three" },
],
};
}
handleDelete = (itemId) => {
// Work with up-to-date state via the callback
this.setState(({educationArray: prevArray}) => {
// Filter out the element you're removing
return {
educationArray: prevArray.filter(({id}) => id !== itemId)
};
});
};
render() {
return (
<ul>
{this.state.educationArray.map((element) => (
<li key={element.id}>
{element.name}{" "}
<input type="button" value="Del" onClick={() => this.handleDelete(element.id)} />
</li>
))}
</ul>
);
}
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Example />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>

remove the selected item from the Table. React

I need to click on the button to delete the line with the selected checkbox.
I don't know how to use the filter method. I read the documentation, but there is little information there. help me correct the code
class Table extends Component {
constructor(props) {
super(props);
this.state = {
droplets: [],
allSelected: false,
isChecked: false
}
this.toggleSelectAll = this.toggleSelectAll.bind(this);
this.toggleSelect = this.toggleSelect.bind(this);
this.handleChecked = this.handleChecked.bind(this);
**this.handleDelete = this.handleDelete.bind(this);**
}
async componentDidMount() {
const res = await fetch('http://api.npoint.io/324f4ca2cdd639760638');
const droplets = await res.json();
this.setState({ 'droplets': droplets })
}
toggleSelect(dropletToToggle) {
this.setState({isChecked: !this.state.isChecked});
this.setState((prevState) => {
const newDroplets = prevState.droplets.map((dplt) => {
if (dplt === dropletToToggle) {
return { ...dplt, checked: !dplt.checked };
}
return dplt;
});
return {
...prevState,
droplets: newDroplets,
allSelected: newDroplets.every((d) => !!d.checked)
};
});
}
toggleSelectAll() {
this.setState({isChecked: !this.state.isChecked});
this.setState((prevState) => {
const toggle = !prevState.allSelected;
const newDroplets = prevState.droplets.map((x) => ({
...x,
checked: toggle
}));
return {
...prevState,
droplets: newDroplets,
allSelected: toggle
};
});
}
handleChecked () {
this.setState({isChecked: !this.state.isChecked});
}
**handleDelete = isChecked => {
this.setState(state => {
const { droplets } = state;
const filteredDroplets = droplets.filter(item => item.id !== isChecked);
return {
droplets: filteredDroplets
};
});
};**
render() {
return (
<div className="body">
<div className="title">Таблица пользователей</div>
<table className="panel">
<Tablehead
toggleSelectAll={this.toggleSelectAll}
allSelected={this.state.allSelected}
/>
<tbody className="row">
<TableBody
droplets={this.state.droplets}
toggleSelect={this.toggleSelect}
/>
</tbody>
</table>
**<button className="button" onClick = {this.handleDelete} >Удалить выбранные</button>**
</div>
);
}
}
the second file in which the item to delete
const TableBody = ({ droplets, toggleSelect}) => {
return (
<>
{droplets.map((droplet, item) => (
<tr className={s.area} key={item.id} >
<td>
<Checkbox
handleClick={() => toggleSelect(droplet)}
isChecked={!!droplet.checked}
/>
</td>
<td>{droplet.num}</td>
<td>{droplet.first_name + " " + droplet.last_name}</td>
<td>{date_form(droplet.date_of_birth)}</td>
<td>{height_form(droplet.height)}</td>
<td>{weight_form(droplet.weight)}</td>
<td>{salary_form(droplet.salary)}</td>
<td>
<button type="submit" className={s.button}>
<Edit />
</button>
</td>
<td>
<button type="submit" className={s.button}>
<Trash />
</button>
</td>
</tr>
))}
</>
);
};
https://codesandbox.io/s/sweet-butterfly-0s4ff?file=/src/Table.jsx
I have altered your sandbox and added some functionality in order to delete the rows.
The comparison is based on full name but you can change it to whatever fits your needs.
https://codesandbox.io/s/keen-silence-i51wz
On your handleDelete function your filter condition seems wrong
Currently you have:
// Filters all droplets that have an id different to the value of isChecked.
const filteredDroplets = droplets.filter(item => item.id !== isChecked);
And it should be
// Returns all the droplets that are not checked
// Meaning that all the checked items are dropped from the array
const filteredDroplets = droplets.filter(item => !item.isChecked);

Show array data in Table rows in ReactJS

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>
))
)

How to remove children objects recursively from object?

I am working on solution
I have created basic tree kind of table whenever user click on expand data related to clicked row will appear under it based on row data
I have achieved basic functionality of expand/collapse upto N nested levels.
But i am stuck with only one problem, so basically all row have conditional expand button based on array having multiple values
Lets say it is split array having 3 entries county,city,state
Default loaded data will be fetched from api, now i have to check array that is there any split available! if yes than i make expand button visible
Consider this scenario
const split = ["country","city","state"]
this is Ui will look like
+ Data_1
+ Data_2
on click of button + new data table row will be rendered based on next split available in our case it is country so visual representation will be like
- Data_1
Country_1
Country_2
+ Data_2
Here country does not have expand button as user have not added next split yet, lets add city, and assume user have clicked Country_1 so data will be like
- Data_1
- Country_1
City_1
City_2
+ Country_2
+ Data_2
My solution works fine till this level now lets say user have removed country from split that all nodes of country and city should be removed and - icon of data_1 should be changed to +
Here is my code
import React, {useState, useEffect, useRef, Fragment} from "react";
import _ from "lodash";
import axios from "axios";
class TableRowData extends React.Component {
state = {
showIcon: false,
selection: [],
data: [],
splitOption: ["campid"]
};
constructor(props) {
super(props);
}
componentDidMount() {
const checkIfSplitExistOnMount = (currentSplit) => {
const i = _.findIndex(this.state.splitOption, function(el) {
return el === currentSplit;
});
if (this.state.splitOption[i + 1]) {
return this.state.splitOption[i + 1];
} else {
return null;
}
}
const getReportData = () => {
axios.get("https://jsonplaceholder.typicode.com/users?_start=0&_limit=1").then((res) => {
const rowData = res.data.map((row) => {
row.name = this.state.splitOption[0];
row.isExpanded = false;
row.currentSplit = this.state.splitOption[0];
row.nextSplit = checkIfSplitExistOnMount(this.state.splitOption[0])
row.parentId = 0;
row.isVisble = true;
//console.log(row)
return row;
});
this.setState({
data: rowData
}, () => { //console.log(this.state.data)
});
});
}
getReportData()
}
render() {
// update state function
const updateState = () => {
this.setState({
data: [...this.state.data],
splitOption: [...this.state.splitOption],
selection: [...this.state.selection],
}, () => {})
}
// recusively update parent and child
const recursion = (obj) => {
let row = obj;
row.isExpanded = row.isExpanded;
row.currentSplit = row.currentSplit;
row.nextSplit = checkIfSplitExist(row.currentSplit)
if (row.children && row.children.length > 0) { // check if has children
row.children.forEach(v => { // if has children do the same recursion for every children
recursion(v);
});
}
return row; // return final new object
}
const recursionDel = (obj,split) => {
var row = obj;
row.currentSplit = row.currentSplit;
row.nextSplit = checkIfSplitExist(row.currentSplit)
if (row.children && row.children.length > 0) { // check if has children
row.children.forEach(v => { // if has children do the same recursion for every children
recursionDel(v);
});
}
return row; // return final new object
}
// function to check if next split is there or not if there than return nextsplit
const checkIfSplitExist = (currentSplit) => {
const i = _.findIndex(this.state.splitOption, function(el) {
return el === currentSplit;
});
if(i !== -1) {
if (this.state.splitOption[i + 1]) {
return this.state.splitOption[i + 1];
} else {
return null;
}
}
}
// recursive update whenever split added
const recursiveUpdate = (data) => {
const prevData = [...data];
return prevData.map((row) => {
const updatedData = recursion(row);
return row;
});
}
// function to delete child and parent node recursively
const recursiveDelete = (data,split) => {
const prevData = [...data];
return prevData.map((row) => {
const data = recursionDel(row,split);
return row;
});
}
const addNewSplit = (split) => {
const i = _.findIndex(this.state.splitOption, function(el) {
return el === split;
});
if(i === -1) {
this.setState(
{
splitOption:[...this.state.splitOption,split]
},
()=>{
var rowData = recursiveUpdate(this.state.data)
this.setState({data:rowData})
}
);
} else {
const prevData = [...this.state.splitOption];
var index = prevData.indexOf(split);
prevData.splice(index,1)
if(index!==-1) {
this.setState(
{
splitOption:prevData
},
()=> {
var rowData = recursiveDelete(this.state.data,split)
this.setState({data:rowData})
}
)
}
}
}
// add lazyload expand data
const ExpandableTableRow = ({rows}) => {
const expandRow = (row) => {
row.children = [
{
id: "_" + Math.random().toString(36).substr(2, 5),
name: row.id + "_" + row.nextSplit,
isExpanded: false,
parentId: row.id,
currentSplit: row.nextSplit,
nextSplit: checkIfSplitExist(row.nextSplit),
isVisble:true
}, {
id: "_" + Math.random().toString(36).substr(2, 5),
name: row.id + "_" + row.nextSplit,
isExpanded: false,
parentId: row.id,
currentSplit: row.nextSplit,
nextSplit: checkIfSplitExist(row.nextSplit),
isVisble:true
}
];
row.isExpanded = true;
updateState();
};
// call whenever - click
const collapseRow = (row) => {
delete row.children;
row.isExpanded = false;
updateState();
};
// toggle
const ExpandCollapsToggle = ({row, expandRow, collapseRow}) => {
// display +/- only if nextsplit is not undefined or null
if (row.nextSplit) {
if (row.isExpanded === true) {
return (<button type="button" onClick={() => collapseRow(row)}>
-
</button>);
} else {
return (<button type="button" onClick={() => expandRow(row)}>
+
</button>);
}
} else {
return null;
}
};
if (rows) {
return rows.map((row) => {
// if(!_.isEmpty(row)) {
return (<Fragment key={row.id}>
<tr key={row.id}>
<td>
<ExpandCollapsToggle row={row} expandRow={expandRow} collapseRow={collapseRow}/>{" "}
{row.split}
- {row.id}
</td>
<td>{row.name}</td>
</tr>
<ExpandableTableRow rows={row.children}/>
</Fragment>);
// }
});
} else {
return null;
}
};
const splitData = this.state.splitOption.map((ob) => {
return (<Fragment key={ob}><span>{ob}</span> > </Fragment>)
})
if (this.state.data) {
return (
<Fragment>
{splitData} <br/>
<button onClick = {()=>addNewSplit("name")}>camp name</button>
<button onClick = {()=>addNewSplit("os")}>os</button>
<button onClick = {()=>addNewSplit("country")}>country</button>
<ExpandableTableRow rows={this.state.data} />
</Fragment>
);
} else {
return null;
}
}
}
export default TableRowData;
Also i have create example of codesandbox.io - Link
Here is how you play with ui to replicate scenario
First click on camp name, expand icon will appear
Now expand if you want to, you can see data according split under
Now add one more split OS or Country and you can see expand icon with 2nd level rows
Next step is to remove "Camp Name", Here is issue when camp name is removed, table should be re render according available splits, in our case user's all row should be removed and + icon must be there are we have next split os or country available, i used default split id, it will be there always
import React, { useState, useEffect, useRef, Fragment } from "react";
import axios from "axios";
const test_data = [{
"id":1,
"name":"Leanne Graham",
"username":"Bret",
"email":"Sincere#april.biz",
"address":{
"street":"Kulas Light",
"suite":"Apt. 556",
"city":"Gwenborough",
"zipcode":"92998-3874",
"geo":{
"lat":"-37.3159",
"lng":"81.1496"
}
},
"phone":"1-770-736-8031 x56442",
"website":"hildegard.org",
"company":{
"name":"Romaguera-Crona",
"catchPhrase":"Multi-layered client-server neural-net",
"bs":"harness real-time e-markets"
}
}];
class TableRowData extends React.Component {
constructor(props) {
super(props);
this.state = {
showIcon: false,
selection: [],
data: [],
splitOption: ["campid"]
};
}
// function to check if next split is there or not if there than return nextsplit
checkIfSplitExist = (currentSplit) => {
const i = this.state.splitOption.indexOf(currentSplit);
if (i > -1 && this.state.splitOption[i + 1]) {
return this.state.splitOption[i + 1];
}
return null;
}
getReportData = () => {
// axios.get("https://jsonplaceholder.typicode.com/users?_start=0&_limit=1").then(({data}) => {
this.setState({
data: test_data.map((row) => {
row.name = this.state.splitOption[0];
row.isExpanded = false;
row.currentSplit = this.state.splitOption[0];
row.nextSplit = this.checkIfSplitExist(this.state.splitOption[0])
row.parentId = 0;
row.isVisble = true;
console.log(row)
return row;
})
});
// });
}
componentDidMount() {
this.getReportData()
}
render() {
// update state function
const updateState = () => {
this.setState({
data: [...this.state.data],
splitOption: [...this.state.splitOption],
selection: [...this.state.selection],
}, () => { })
}
const recursionUpdateAndDeleteRow = (parentRow, childRow, split, index = 0) => {
childRow.children && childRow.children.forEach((r) => {
recursionUpdateAndDeleteRow(childRow, r, split, index + 1);
});
if (parentRow && split.indexOf(childRow.currentSplit) == -1) {
delete parentRow.children;
}
childRow.currentSplit = split[index];
childRow.nextSplit = split[index + 1] || null;
if (!childRow.children) {
childRow.isExpanded = false;
}
}
const recursionUpdateAndDeleteRows = (rows, split) => {
const _copy = [...rows];
_copy.forEach((row) => {
recursionUpdateAndDeleteRow(null, row, split);
});
return _copy;
}
const toggleSplit = (split) => {
const index = this.state.splitOption.indexOf(split);
let currentSplitOptions = [...this.state.splitOption];
if (index > -1) {
currentSplitOptions.splice(index, 1)
}
else {
currentSplitOptions.push(split);
}
const _data = recursionUpdateAndDeleteRows(this.state.data, currentSplitOptions);
this.setState({
splitOption: currentSplitOptions,
data: _data
})
}
// add lazyload expand data
const ExpandableTableRow = ({ rows }) => {
const expandRow = (row) => {
row.children = [
{
id: "_" + Math.random().toString(36).substr(2, 5),
name: row.id + "_" + row.nextSplit,
isExpanded: false,
parentId: row.id,
currentSplit: row.nextSplit,
nextSplit: this.checkIfSplitExist(row.nextSplit),
isVisble: true
}, {
id: "_" + Math.random().toString(36).substr(2, 5),
name: row.id + "_" + row.nextSplit,
isExpanded: false,
parentId: row.id,
currentSplit: row.nextSplit,
nextSplit: this.checkIfSplitExist(row.nextSplit),
isVisble: true
}
];
row.isExpanded = true;
updateState();
};
// call whenever - click
const collapseRow = (row) => {
delete row.children;
row.isExpanded = false;
updateState();
};
// toggle
const ExpandCollapsToggle = ({ row }) => {
// display +/- only if nextsplit is not undefined or null
if (row.nextSplit) {
if (row.isExpanded) {
return (
<button type="button" onClick={() => collapseRow(row)}>
-
</button>
);
}
return (
<button type="button" onClick={() => expandRow(row)}>
+
</button>
);
}
return null;
};
if (rows) {
return rows.map((row) => {
return (
<Fragment key={row.id}>
<tr key={row.id}>
<td>
<ExpandCollapsToggle
row={row}
/>
{" "}{row.split} - {row.id}
</td>
<td>{row.name}</td>
</tr>
<ExpandableTableRow rows={row.children} />
</Fragment>
);
});
} else {
return null;
}
};
if (this.state.data) {
return (
<Fragment>
{this.state.splitOption.join(', ')} <br />
<button onClick={() => toggleSplit("name")}>
camp name
</button>
<button onClick={() => toggleSplit("os")}>os</button>
<button onClick={() => toggleSplit("country")}>country</button>
<br />
<ExpandableTableRow rows={this.state.data} />
</Fragment>
);
} else {
return null;
}
}
}
export default function App() {
return (
<div>
<TableRowData />
</div>
);
}
Here working example

Array not getting cleared to null or empty in setState on click in react

Array not getting cleared to null or empty in setState on click in react.
When I click on the submit button, the array must be set to []. It is setting to [], but on change the previous array of items comes into the array.
let questions = [];
let qns = [];
class App extends Component {
constructor(props) {
super(props);
this.state = {
btnDisabled: true,
//questions: [],
};
}
changeRadioHandler = (event, j) => {
this.setState({ checked: true });
const qn = event.target.name;
const id = event.target.value;
let idVal = this.props.dat.mat.opts;
let text = this.props.dat.mat.opt;
let userAnswer = [];
for (let j = 0; j < text.length; j++) {
userAnswer.push(false);
}
const option = text.map((t, index) => ({
text: t.text,
userAnswer: userAnswer[index],
}));
const elIndex = option.findIndex((element) => element.text === id);
const options = { ...option };
options[elIndex] = {
...options[elIndex],
userAnswer: true,
};
const question = {
options,
id: event.target.value,
qn,
};
questions[j] = options;
qns = questions.filter((e) => {
return e != null;
});
console.log(qns, qns.length);
this.setState({ qns });
if (qns.length === idVal.length) {
this.setState({
btnDisabled: false,
});
}
};
submitHandler = () => {
console.log(this.state.qns, this.state.questions);
this.setState({ qns: [] }, () =>
console.log(this.state.qns, this.state.questions)
);
};
render() {
return (
<div class="matrix-bd">
{this.props.dat.mat && (
<div class="grid">
{this.props.dat.mat.opts.map((questions, j) => {
return (
<div class="rows" key={j}>
<div class="cell main">{questions.text}</div>
{this.props.dat.mat.opt.map((element, i) => {
return (
<div class="cell" key={i}>
<input
type="radio"
id={j + i}
name={questions.text}
value={element.text}
onChange={(event) =>
this.changeRadioHandler(event, j)
}
></input>
<label htmlFor={j + i}>{element.text}</label>
</div>
);
})}
</div>
);
})}
</div>
)}
<div>
<button
type="button"
class="btn btn-primary"
disabled={this.state.btnDisabled}
onClick={this.submitHandler}
>
SUBMIT
</button>
</div>
</div>
);
}
}
export default App;
On button click submit, the array must be set to [] and when on change, the value must be set to the emptied array with respect to its index.
changeRadioHandler = (event, j) => {
// the better way is use local variable
let questions = [];
let qns = [];
...
}
submitHandler = () => {
console.log(this.state.qns, this.state.questions);
this.setState({ qns: [] }, () =>
console.log(this.state.qns, this.state.questions)
)}
// clear the previous `qns`
// if u use local variable. you don't need those lines
// this.qns = []
// this.questions = []
}
Finally, found out the solution.
After adding componentDidMount and setting questions variable to null solved my issue.
componentDidMount = () => {
questions = [];
};
Thanks all for your efforts and responses!

Categories