How to handle event bubbling in Javascript - javascript

I implemented a toggle button for a list of items. To display the items and the toggle button, I run them through a loop given below.
renderRowValue = (values) => {
const sourceId = values.id;
const payload = {id: values.id, isActive: !values.isActive };
return (
<tr>
<td key = {sourceId}>
{values.name}
</td>
<td>
<SwitchToggleButton
onChange={this.handleToggleChange(payload)}
defaultChecked={values.isActive}
label={"version2"}
/>
</td>
</tr>
)
};
Then I call the function to display the value in the renderRowValue like this below:
<Grid xs={12} sm={12} md={10} className="shadow-border">
<Row>
<Table striped bordered hover>
<thead>
<tr>
<th>Source</th>
</tr>
</thead>
<tbody>
{values.map(this.renderRowValue)}
</tbody>
</Table>
</Row>
</Grid>
But the main issue is that when I toggle a button on the value of the others changes.For example, I turn the first item on, then everything else turns on automatically, the same for when I turn it off.
The function to handle toggle switch can be found below:
handleToggleChange =(values) => {
this.props.dispatch(values.isActive(values));
};
Please how can I fix this issue to just turn it on for the first item without affecting the rest?

assuming the handleToggleChange is in the same class as your component.
Update it so that it's a closure
handleToggleChange = (payload) = (e) => {
e.preventDefault()
this.props.dispatch(values.isActive(payload));
};
// renderRowValue
<SwitchToggleButton
onChange={this.handleToggleChange(payload)}
defaultChecked={values.isActive}
label={"version2"}
/>

This is how I've done it trying to stick as close to your code as possible without actually seeing all of your code.
https://gist.github.com/leslie-alldridge/5ffd25cfbd48c3134495b2245aa9d7ad

Related

React Function filter does not work (no errors in the console)

In my list, when I click on row, the background of the changes color and the id of my row is added to the array of my state. It works, but when I do the reverse my array doesn't get empty when I use the filter function (line 15).
import React, {useState} from 'react';
import './Liste.css';
import Button from '../Button/Button';
function Liste(props) {
const [nbLine, setNbLine] = useState([]);
const clickLine = (e) =>
{
if (e.target.parentNode.className.length > 0)
{
console.log(e.target.parentNode.id);
e.target.parentNode.classList.remove("lineClicked");
nbLine.filter(line => line != e.target.parentNode.id);
console.log(nbLine);
}
else
{
e.target.parentNode.classList.add("lineClicked");
nbLine.push(e.target.parentNode.id);
console.log(nbLine);
}
}
const doubleClickLine = () =>
{
console.log("doubleClickLine");
}
return (
<>
<table className='tableList'>
<thead>
<tr>
{props.headers.map((header, h) =>
<th key={h}>{header}</th>
)}
</tr>
</thead>
<tbody>
{props.records.map((record, r) =>
<tr key={r} id={props.table+"_"+record[0]} onClick={clickLine} onDoubleClick={doubleClickLine}>
{props.columns.map((column, c) =>
<td key={c}>{record[column]}</td>
)}
</tr>
)}
</tbody>
<tfoot>
<tr>
<th colSpan={7}>
{props.buttons.map((button, b) =>
<Button key={b} id={button[0]} type={button[1]} value={button[2]} click={button[3]}/>
)}
</th>
</tr>
</tfoot>
</table>
</>
)
}
export default Liste;
Here is the screen when I click (the elements are in the table).
Note: the data is fictitious.
And here is the screen when I click again (the elements resent in the array).
And here is the screen when I click again (the elements resent in the array).
Why is the filter function not working?
I unlocked myself.
By replacing the state array with an object.
const [nbLines, setNbLines] = useState({});
By using the delete operator instead of the filter method.
delete nbLines[e.target.parentNode.id];
And by assigning a value to a key instead of the push method.
nbLines[e.target.parentNode.id] = e.target.parentNode.id;

React: How to deal with large td Input element in Table that re-render everytime when state changed occurs?

I'm not sure if this is the right way to push table element in React, but I've been doing this all the time. So this for loop below will get executed everytime re-render occurs or in this case when I change my input. what if I have like 100 Input?
Questions:
Is this the right way to draw table element?
wouldn't re-render cause bad performance especially if you have some kind of loop before return?
If this is not the right way to do it. Please show me the right way to do it.
function App() {
const [inputs,setInputs] = useState({input1:'',input2:'',input3:''})
function handleOnChange(e){
const {name,value} = e.target
setInputs(prev => ({...prev, [name]:value}))
}
let tableElement = []
for(let i = 1; i <= 3; i++){
tableElement.push(<tr key={i}>
<td><Input value={inputs[`${i}`]} name={`input${i}`} onChange={handleOnChange} /></td>
</tr>)
}
return (
<div>
<Table>
<tbody>
{tableElement}
</tbody>
</Table>
</div>
);
}
export default App;
Given code:
let tableElement = []
for (let i = 1; i <= 3; i++){
tableElement.push((
<tr key={i}>
<td>
<Input value={inputs[`${i}`]} name={`input${i}`} onChange={handleOnChange} />
</td>
</tr>
))
}
return (
<div>
<Table>
<tbody>
{tableElement}
</tbody>
</Table>
</div>
);
Questions:
Is this the right way to draw table element?
It isn't wrong, though it may not be the most common way to render out an array to JSX. Typically you may opt to map an array to JSX
const tableElement = [...Array(3).keys()].map((i) => (
<tr key={i}>
<td>
<Input value={inputs[`${i}`]} name={`input${i}`} onChange={handleOnChange} />
</td>
</tr>
));
return (
<div>
<Table>
<tbody>
{tableElement}
</tbody>
</Table>
</div>
);
Or directly in the JSX
return (
<div>
<Table>
<tbody>
{[...Array(3).keys()].map((i) => (
<tr key={i}>
<td>
<Input
value={inputs[`${i}`]}
name={`input${i}`}
onChange={handleOnChange}
/>
</td>
</tr>
))}
</tbody>
</Table>
</div>
);
wouldn't re-render cause bad performance especially if you have some kind of loop before return?
I suppose there is a potential for poor performance, but due to the nature of the UI needing to map out a bunch of array elements when the component renders this is work that would be necessary anyway.
React is already pretty optimized out-of-the-box. It uses a reconciliation process to determine what mapped elements actually changed from the previous render and only rerenders what is necessary. The React key is used when mapping arrays to help in this regard.
If this is not the right way to do it. Please show me the right way to do it.
I shared the more common methods of mapping an array to JSX in point #1 above.

React/preact OnClick <td> render table with details

I have a Table which when you click a td tag that is an plusbutton it should show the details about that row. Something like this:
Right now I am just testing it like this:
props.info.map((l, i) => {
return (
<tr>
<td>{i + 1}</td>
<td>{l.UserId}</td>
<td onClick={props.onShowInfoDetails}>
<MenuPlusButton /></td>
{props.showInfoDetails && (
<DetailsTable />
)
}
</tr>
)
})
where the DetailsTable is the thing i want to render onClick
export const DetailsTable = (props: Props) => {
return (
<tr>
<td>Hello</td>
</tr>
)
}
There is two problems with this. First the DetailsTable renders to the right of the rest of the content and not under it like in the picture. second problem is that when I click it all table rows show the hello not just the one that I clicked. Both of these I can't seem to figure out. The second problem I guess is because it says if props.showEntryDetails is true it renders the DetailsTable and the onClick sets it to true but how do I make it so it's only true for that row that I clicked?

Dynamically Creating Table Rows With React

I am trying to create a Table using React and React-Bootstrap that has a custom number of table rows. The table is supposed to store data about player statistics of a certain video game, and based on the video game the statistics may change, thus the number of rows and titles of these rows must be able to dynamically change as well. I wanted to create an array in the state that held the list of current statistics, then map this array to a element using the map function and render the table. However, after trying several approaches I can't get any of the custom input to render. Below is the code :
Class Structure
class Statistics extends Component {
constructor(props) {
super(props)
this.state = {
game: '',
player_names: [],
positions: [],
stat_categories: [
'kills',
'deaths',
'assists'
]
}
}
renderTableRows(array) {
return (
<tr>
<th> NAME </th>
<th> TEAM </th>
<th> POSITION </th>
{ array.map(item => {
console.log(item)
<th key={item}> {item} </th>
})
}
</tr>
)
}
render() {
const columnLength = this.state.player_names.length
const statCols = this.state.stat_categories
return (
<div>
<MyNav url={this.props.location.pathname} />
<Table responsive striped bordered hover>
<thead>
{ this.renderTableRows(statCols) }
</thead>
</Table>
</div>
)
}
}
The console also properly logs the data in state (kills, deaths, assists) -- so the issue is when rendering the element. Any help would be appreciated!
You have no return statement in your map function, inside of renderTableRows.
When using ES6 arrow functions, you can either:
Return data directly without a return statement
(args) => (returnedData);
Or add some logic instead of just returning directly,
(args) => {
// Logic here
return returnedData
}
In the second case you'll need a return statement, because you are logging, if you choose to remove logging, go the first way.
Also, please post the code directly in your question, as using an image makes it less readable and not indexed by search engines.
You have to render each item in separate trs, not as a series of ths
renderTableCols(array) {
return array.map(item => <th>{item}</th>)
}
renderTableColValues(item, cols) {
return cols.map(col => <td>{item[col]}</td>)
}
renderTableRows(array) {
return array.map(item =>
<tr>
<td>{item.name}</td>
<td>{item.team}</td>
<td>{item.position}</td>
{this.renderTableColValues(item, this.cols)}
</tr>
);
}
render() {
return (
<Table>
<thead>
<tr>
<th>NAME</th>
<th>TEAM</th>
<th>POSITION</th>
{this.renderTableCols(this.cols)}
</tr>
</thead>
<tbody>
{this.renderTableRows(items)}
</tbody>
</Table>
);
}
More on tables https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table
I will give you a similar answer of what youre encoutering but its kinda different approach with a excelent solution
So, you are trying to create a dynamic table but youre making table rows static, what i did was letting the table to receive arrays of head and data and then create as many rows or datas that are required.
heres the code
export function objectIntoTableData(object) {
return Object.values(object).map((data, index) => {
return <td key={index}>{data}</td>;
});
}
You must change this index to (value,index) => , thats just my use
tableRows(data) {
return data.map(value => {
return <tr key={value.index}>{objectIntoTableData(value)}</tr>;
});
}
<thead>
<tr>
{head.map((value, index) => {
return <th key={index}>{value}</th>;
})}
</tr>
</thead>
<tbody>{this.tableRows(data)}</tbody>
Rather use a id or index inside your object since the index callback of the map function, its unsafe to use for the keys.
<ReactTableUse
head={["#", "Cell1", "Cell2", "Cell3"]}
data={[{id:1, test:1},{id:2, test:2}]}
/>
Rules:
When your state changes, render method of a class based component will be called.
Question: Who will change the state? will it grow inside the component ? What is your problem ? your are not being able to render anything ? or statistics is not dynamically rendering ? if you want to change it dynamically , you need to change the state first.

How do you remove a parent table row when child "delete" button is clicked in React JS?

I am rendering table data in React JS and am having trouble getting my table row to go hidden on click of a child "delete" button. My current handler and render functions looks like this:
...
changeHandler: function(e) {
...
},
deleteHandler: function(e) {
e.currentTarget.closest("tr").style.visibility = "hidden";
},
render: function() {
return (
<div>
<div class="container">
<select onChange={ this.changeHandler.bind(this) }>
<option></option>
...
</select>
<table>
<thead>
<tr>
...
</tr>
</thead>
<tbody>
{data.map(function(row, j) {
return <tr key={j}>
<td>{row.text}</td>
<td><a href="" onClick={this.deleteHandler.bind(this, j)}>delete</a></td>
</tr>
}
)}
</tbody>
</table>
</div>
</div>
);
}
...
When I click on the delete anchor, I get this error in the console:
Uncaught TypeError: Cannot read property 'bind' of undefined
I don't understand why my delete handler isn't being recognized and bound, when the changeHandler I'm using is. Could somebody please tell me how to get this event to hit the handler and how to target the parent tr to have it hidden?
After the correction in the typo above, I see that was not the error here. Check out the following fiddle to see it in action. There will be some need of style changes when you hide the row.
https://jsfiddle.net/vgo52rey/
The problem is with the binding of 'this' in the filter function in the fiddle. Abstract that out into another method so you can store the reference to this in a different variable, then you can keep the reference to this.delete or this.deleteHandler.
delete: function(e) {
e.currentTarget.closest("tr").style.visibility = "hidden";
},
renderRows: function() {
var shouldIRender =(row) => (this.state.filter === row.status || this.state.filter === "");
var self = this
return requests.filter(shouldIRender).map(function(row, j) {
return <tr key={j}>
<td style={tdStyle}>{row.title}</td>
<td style={tdStyle}>{row.status}</td>
<td style={tdStyle}>{row.created_at}</td>
<td style={tdStyle}>{row.updated_at}</td>
<td style={tdStyle}><a href="#" onClick={self.delete}>delete</a></td>
</tr>
}
)
},
in your render method now you can just supply this renderRows method return value:
<tbody>
{this.renderRows()}
</tbody>

Categories

Resources