In the following line:
checked={this.state.peopleChecked.some(({ asset}) => asset['object'] ['user']['id'] === person.userCompetences.map((user, index) => {
user['asset']['id']
})
)}
I have a problem comparing two objects.
Compares a property from the array people ->userCompetences -> asset ->id with an object from the array peopleChecked ->asset -> object ->user - > asset_id.
if id from arraypeople and asset_id, id === asset_id are equal to returnstrue. Checkbox is checked
Code here: https://stackblitz.com/edit/react-n2zkjk
class App extends Component {
constructor() {
super();
this.state = {
people: [
{
firstname: "Paul",
userCompetences: [
{ asset:{
id: "12345"
}
}
]
},
{
firstname: "Victor",
userCompetences: [
{ asset: {
id: "5646535"
}
}
]
},
{
firstname: "Martin",
userCompetences: [
{ asset: {
id: "097867575675"
}
}
]
},
{
firstname: "Gregor",
userCompetences: [
{ asset: {
id: "67890"
}
}
]
}
],
peopleChecked: [
{
amount: 0,
asset: {
id: "fgfgfgfg",
object: {
competence: null,
id: "dsdsdsdsd",
user: {
firstname: "Gregor",
asset_id: "67890"
}
}
}
},
{
amount: 0,
asset: {
id: "dsdsdsd",
object: {
competence: null,
id: "wewewe",
user: {
firstname: "Paul",
asset_id: "12345"
}
}
}
},
],
selectPeopleId: []
}
}
/*handleSelect = (person) => {
//Check if clicked checkbox is already selected
var found = this.state.peopleChecked.find((element) => {
return element.id == person.id;
});
if(found){
//If clicked checkbox already selected then remove that from peopleChecked array
this.setState({
peopleChecked: this.state.peopleChecked.filter(element => element.id !== person.id),
selectPeopleId: this.state.selectPeopleId.filter(element => element !== person.id)
}, () => console.log(this.state.peopleChecked))
}else{
//If clicked checkbox is not already selected then add that in peopleChecked array
this.setState({
selectPeopleId: [...this.state.selectPeopleId, person.id],
peopleChecked: [...this.state.peopleChecked,person]
}, () => {console.log(this.state.selectPeopleId);console.log(this.state.peopleChecked);})
}
}*/
render() {
return (
<div>
{this.state.people.map(person => (
<div key={person.id} className="mb-1">
<input
type={'checkbox'}
id={person.id}
label={person.firstname}
checked={this.state.peopleChecked.some(({ asset}) => asset['object']['user']['id'] === person.userCompetences.map((user, index) => {
user['asset']['id']
})
)}
onChange = {() => this.handleSelect(person)}
/> {person.firstname}
</div>
))}
</div>
);
}
}
Your correct checked code syntax would be below based on your data structure:
Issue was asset_id correct key was missing and map returns an array thus you would need its index, however in your case you can simply swap it with person.userCompetences.[0]['asset']['id'] but I kept your syntax in case you want it for some other purpose.
checked={
this.state.peopleChecked.some(
({ asset }) => asset['object']['user']['asset_id'] === person.userCompetences.map(
(user, index) => user['asset']['id']
)[0]
)}
However its inherently complicated and you should focus on untangling it by placing some cached const in your map function to keep track of what you are looking at. I would also advice to introduce some child component to render in the first map to make your life easier maintaining this code in the future.
Edited code: https://stackblitz.com/edit/react-ptsnbc?file=index.js
Related
During the React.js course I'm doing, I was tasked with making a simple fortune-teller app. Theoretically, everything works as planned, but I did the task differently than the tutor. Instead of a simple fortune-telling table, I've created an array of objects, each with its id and 'omen'. The problem arose when after adding a new 'omen' an alert should be displayed that gives the current content of 'omens' in state. Only the previous values appear, without the added value. I will be grateful for the hints. In the original design, this problem does not occur, although it is very similar.
class Draw extends React.Component {
state = {
index: "",
value: "",
omens: [
{ id: 1, omen: "Hard work pays off" },
{ id: 2, omen: "You will be rich" },
{ id: 3, omen: "Be kind to others" },
],
};
handleDrawOmen = () => {
const index = Math.floor(Math.random() * this.state.omens.length + 1);
this.setState({
index: index,
});
};
showOmen = () => {
let omens = this.state.omens;
omens = omens.filter((omen) => omen.id === this.state.index);
return omens.map((omen) => (
<h1 id={omen.id} key={omen.id}>
{omen.omen}
</h1>
));
};
handleInputChange = (e) => {
this.setState({
value: e.target.value,
});
};
handleAddOmen = () => {
if (this.state.value === "") {
return alert("Enter some omen!");
}
const omens = this.state.omens.concat({
id: this.state.omens.length + 1,
omen: this.state.value,
});
this.setState({
omens,
value: "",
});
console.log(this.state.omens);
alert(
`Omen added. Actual omens: ${this.state.omens.map(
(omen) => omen.omen
)}`
);
};
render() {
return (
<div>
<button onClick={this.handleDrawOmen}>Show omen</button>
<br />
<input
placeholder="Write your own omen..."
value={this.state.value}
onChange={this.handleInputChange}
/>
<button onClick={this.handleAddOmen}>Add omen</button>
{this.showOmen()}
</div>
);
}
}
ReactDOM.render(<Draw />, document.getElementById("root"));
The state object is immutable. So you need to create your new array and apply it afterwards:
const omens = [
...this.state.omens,
{
id: this.state.omens.length + 1,
omen: this.state.value,
}
]
also setState is async so you need to wait until it finished:
this.setState({
omens,
value: "",
}, () => {
alert(
`Omen added. Actual omens: ${this.state.omens.map(
(omen) => omen.omen
)}`
)
https://reactjs.org/docs/react-component.html#setstate
Hey guys I am using this table to display data and I added a button to each row. How would I be able to hide a row when I click the hide button next to it?
I am aware of a way to do within html elements but not sure how to hide a particular row within a table thats within a loop
Can anyone show me how to accomplish this?
Thank you
import React, { Component } from 'react'
class Table extends Component {
constructor(props) {
super(props) //since we are extending class Table so we have to use super in order to override Component class constructor
this.state = { //state is by default an object
students: [
{ id: 1, name: 'Wasif', age: 21, email: 'wasif#email.com' },
{ id: 2, name: 'Ali', age: 19, email: 'ali#email.com' },
{ id: 3, name: 'Saad', age: 16, email: 'saad#email.com' },
{ id: 4, name: 'Asad', age: 25, email: 'asad#email.com' }
]
}
}
renderTableData() {
return this.state.students.map((student, index) => {
const { id, name, age, email } = student //destructuring
return (
<tr key={id}>
<td>{id}</td>
<td>{name}</td>
<td>{age}</td>
<td>{email}</td>
<td><button>HIDE</button></td>
</tr>
)
})
}
renderTableHeader() {
let header = Object.keys(this.state.students[0])
return header.map((key, index) => {
return <th key={index}>{key.toUpperCase()}</th>
})
}
render() { //Whenever our class runs, render method will be called automatically, it may have already defined in the constructor behind the scene.
return (
<div>
<h1 id='title'>React Dynamic Table</h1>
<table id='students'>
<tbody>
<tr>{this.renderTableHeader()}</tr>
{this.renderTableData()}
</tbody>
</table>
</div>
)
}
}
export default Table
You could add an onClick handler to the button that adds a property that determines the student should be hidden or not.
Notice the onClick={() => this.hideRow(id)} below.
renderTableData() {
return this.state.students.map((student, index) => {
const { id, name, age, email, isHidden } = student; //destructuring
// isHidden will default to undefined if not found on the student object
// user is hidden
if (isHidden === true) {
return null;
}
return (
<tr key={id}>
<td>{id}</td>
<td>{name}</td>
<td>{age}</td>
<td>{email}</td>
<td>
<button onClick={() => this.hideRow(id)}>HIDE</button>
</td>
</tr>
);
});
}
The hideRow method will accept a student id and will add an isHidden: true attribute to the student with that id.
hideRow(id) {
const students = this.state.students.map((student) => {
// not same id? leave as is
if (student.id !== id) {
return student;
}
return { ...student, isHidden: true };
});
this.setState({ students });
}
Now you don't want to display the isHidden column, so you have to update renderTableHeader method to skip that.
renderTableHeader() {
let header = Object.keys(this.state.students[0]);
return header.map((key, index) => {
// notice this
if (key === "isHidden") {
return null;
}
return <th key={index}>{key.toUpperCase()}</th>;
});
}
Add a isVisible key in all objects like
students: [
{ id: 1, name: 'Wasif', age: 21, email: 'wasif#email.com', isVisible: true },
{ id: 2, name: 'Ali', age: 19, email: 'ali#email.com', isVisible: true },
{ id: 3, name: 'Saad', age: 16, email: 'saad#email.com', isVisible: true },
{ id: 4, name: 'Asad', age: 25, email: 'asad#email.com', isVisible: true }
]
Then in your render row function do this
renderTableData() {
return this.state.students.map((student, index) => {
const { id, name, age, email, isVisible } = student
return isVisible ? (
<tr key={id}>
<td>{id}</td>
<td>{name}</td>
<td>{age}</td>
<td>{email}</td>
<td><button>HIDE</button></td>
</tr>
) : null
})
On button/row click update state.
Try this code
import React, { Component } from "react";
class Table extends Component {
constructor(props) {
super(props); //since we are extending class Table so we have to use super in order to override Component class constructor
this.state = {
//state is by default an object
students: [
{ id: 1, name: "Wasif", age: 21, email: "wasif#email.com", toggle: true},
{ id: 2, name: "Ali", age: 19, email: "ali#email.com", toggle: true },
{ id: 3, name: "Saad", age: 16, email: "saad#email.com", toggle: true},
{ id: 4, name: "Asad", age: 25, email: "asad#email.com", toggle: true }
]
};
}
handleClick(index) {
let students = [...this.state.students];
students[index].toggle = !students[index].toggle;
this.setState({ students });
}
renderTableData() {
return this.state.students.map((student, index) => {
const { id, name, age, email, toggle } = student; //destructuring
if (toggle) {
return (
<tr key={id}>
<td>{id}</td>
<td>{name}</td>
<td>{age}</td>
<td>{email}</td>
<`td`>
<button
value={index}
onClick={(e) => this.handleClick(e.target.value)}
>
Hide
</button>
</td>
</tr>
);
} else {
return null;
}
});
}
renderTableHeader() {
let header = Object.keys(this.state.students[0]);
return header.map((key, index) => {
return <th key={index}>{key.toUpperCase()}</th>;
});
}
render() {
//Whenever our class runs, render method will be called automatically, it may have already defined in the constructor behind the scene.
return (
<div>
<h1 id="title">React Dynamic Table</h1>
<table id="students">
<tbody>
<tr>{this.renderTableHeader()}</tr>
{this.renderTableData()}
</tbody>
</table>
</div>
);
}
}
export default Table;
Follow these steps:
Put an onclick on the button
Pass the array as props to the component
On the next component display the array
Add the onclick method to it which is also passed as a props from the main component(Pass id as a parameter)
In the method use a filter array to remove the row of your choice when you click it.
The code is as follow:
https://codesandbox.io/s/modern-tdd-mlmzl?file=/src/components/Table.js
I'm having list of table and each row is clickable. Once I onclick the row need to add corresponding data to array. If I again click the same row need to remove the data from array. Likewise i need to add an toggle Active class for selected row.
const data = [
{
name: "Name1",
foramt: "pdf",
version: 0
},
{
name: "Name2",
foramt: "pdf",
version: 0
},
{
name: "Name3",
foramt: "pdf",
version: 2
},
{
name: "Name4",
foramt: "pdf",
version: 5
},
]
this.state = {
active: false,
activeIndex: null,
selectedRow: []
}
<Table>
data.map((item, index) => {
const rowSelected = this.state.activeIndex === index ? "row-selected" : "";
return
<TableRow className = {rowSelected} onClick={this.handleClick(item,index)}>
<Cell>{item.name}</Cell>
<Cell>{item.format}</Cell>
</TableRow>
})
</Table>
handleClick = (item,index) => {
const {activeIndex} = this.state;
let array = []
if(activeIndex !== index) {
array.push(item);
}
this.setState({
selectedRows: array
})
}
For the TableRow onCLick event:
Change this:
onClick={this.handleClick(item,index)}
To
onClick={() => this.handleClick(item, index)}
The first case will run immediately instead of waiting for the event to be called.
And for the className
Change this:
className={rowSelected}
To:
className={rowSelected ? "row-selected" : null}
In the first one when the rowSelected === true you'd get className={true} which doesn't really point to any class name.
In the second example though you'd get className="selected"
I'm trying to fetch data from my local json file.
Fetching the data don't give errors and everything is returning correctly in a console log.
When I try to set that data into a state, it works if I parse json and not json.data.
json.data don't do anything, but json works, only if I use the map function it gives me a bunch of errors
Code :
getData = () => {
fetch('http://localhost:3000/ingredients.json')
.then(response => {
if (response.ok) {
return response;
} else {
let response = `${response.statusText}`
let errorMessage = `${response.status(response)}`
let error = new Error(errorMessage)
throw (error)
}
})
.then(response => response.json())
.then(json => {
console.log(json);
this.setState({ data: json })
console.log(this.state)
});
}
render() {
return (
<div>
{
this.state.data &&
this.state.data.map((key, display, nutrition) =>
<div key={key}>
{display}
{nutrition}
</div>
)}
</div>
)
}
Here are my error :
Objects are not valid as a React child (found: object with keys {key, display, unity, category, nutrition, vitamins}). If you meant to render a collection of children, use an array instead.
The map function of Array.prototype accepts a function callback with args, (value, index) => {}. If you plan is to destructure, use instead
this.state.data.map({key, display, nutrition, ...rest}, index) => { // index of value in this.sta
<div key={key}>
{display} //I am assuming this is a string not a object or array
{nutrition} // this should be a string
</div>
}
Edit:
I am assuming data is like
data: [
{
key: "",
nutrition: "",
display: ""
},
{
key: "",
nutrition: "",
display: ""
}
]
Edit 2:
Given this
state {
data =
[
{ id: 1,
display: 'Pommes',
unity: [ 'unités' ],
category: [ 'fruits' ],
nutrition: 95,
vitamins: [ 'C', 'B-6' ]
},
{
id: 2,
display: 'Poires',
unity: [ 'unités' ],
category: [ 'fruits' ],
nutrition: 95,
vitamins: [ 'C', 'B', 'E' ]
}
];
}
Here is how to display it:
render() {
return (
<>
{
this.state.data && this.state.data.map(({id, display, nutrition, vitamins}) => {
return (
<div key={id}>
{display}
{nutrition}
{<>vitamins.map(v => v) </>} //in case you need thing such as vitamis, Array, also to eppear
</div>
)})
}
</>
)
}
If your data is not array don't use map function. Change your code like below:
render() {
return (
<div>
{
this.state.data &&
<div key={this.state.data.key}>
{this.state.data.display}
{this.state.data.nutrition}
</div>
}
</div>
)
}
I'm able to make the filter works but simply use the filter of es6, but then the problem is I don't know how to reset the state back to the original source.
Usually the data source is an API calls but it possible to make avoid api call when the user deleted the value from the filter input?
const data = [
{
Id: "1",
FirstName: "Luke",
LastName: "Skywalker"
},
{
Id: "2",
FirstName: "Darth",
LastName: "Vader"
},
{
Id: "3",
FirstName: "Leia",
LastName: "Organa"
},
{
Id: "4",
FirstName: "Owen",
LastName: "Lars"
}
];
class App extends React.Component {
constructor() {
super()
this.state = {
data: data
}
}
filterId(e) {
const Id = e.target.value
if (Id) {
this.setState({
data: this.state.data.filter(v => v.Id === Id),
})
} else {
this.setState({
data
})
}
}
render() {
return (
<div style={styles}>
<table>
<th>Id <input type="number" onChange={e => this.filterId(e)} /></th>
<th>Name<input /></th>
{this.state.data.map((o, i) => {
return (
<tr>
<td>{o.Id}</td>
<td>{o.FirstName}</td>
</tr>
)
})}
</table>
</div>
);
}
}
https://codesandbox.io/s/oo22451v25
You may either store filtered data in separate variable or do data filtering dynamically in render function, like this.state.data.filter(v => v.Id === this.state.Id).map(...).
First up: You're going to want to filter using includes() for usability. Otherwise things won't match until they're 100% identical. This will matter once you start to deal with fields longer than one digit.
Secondly: Filtration via the render() method will allow you to search more robustly, ie allow you to backspace to unfilter, as this.state.data will remain pristine.
See below for a practical example.
// Data.
const data = [
{
Id: "1",
FirstName: "Luke",
LastName: "Skywalker"
},
{
Id: "2",
FirstName: "Darth",
LastName: "Vader"
},
{
Id: "3",
FirstName: "Leia",
LastName: "Organa"
},
{
Id: "4",
FirstName: "Owen",
LastName: "Lars"
}
]
// Filter.
class Filter extends React.Component {
// Constructor.
constructor(props) {
super(props)
this.state = {data, query: ''}
}
// Render.
render() {
return (
<div>
<table>
<tr>
<input placeholder="Query" type="number" value={this.state.query} onChange={event => this.setState({query: event.target.value})} />
</tr>
<th>Id</th>
<th>Name</th>
{this.state.data.filter((point) => point.Id.includes(this.state.query)).map((o, i) => {
return (
<tr>
<td>{o.Id}</td>
<td>{o.FirstName}</td>
</tr>
)
})}
</table>
</div>
)
}
}
ReactDOM.render(<Filter/>, document.querySelector('#root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
There are several thing you could do, but I would advice not to change the original data stored in the state, but rather add a new state property which holds the data when the original data is filtered.
You could then assign this new state object to a variable and output it this way.
It could then look somethiing like this:
const data = [
{
id: '1',
firstName: 'Luke',
LastName: 'Skywalker',
},
{
id: '2',
firstName: 'Darth',
LastName: 'Vader',
},
{
id: '3',
firstName: 'Leia',
LastName: 'Organa',
},
{
id: '4',
firstName: 'Owen',
LastName: 'Lars',
},
];
class App extends React.Component {
constructor() {
super();
this.state = {
data: data,
filteredData: null,
};
this.filterId = this.filterId.bind(this);
}
filterId(e) {
const id = e.target.value;
if (id) {
this.setState({
filteredData: this.state.data.filter(v => v.id === id),
});
} else {
this.setState({
filteredData: null,
});
}
}
render() {
const dataOutput = this.state.filteredData || this.state.data;
return (
<div>
<table>
<th>
id <input type="number" onChange={e => this.filterId(e)} />
</th>
<th>
Name<input />
</th>
{dataOutput.map((o, i) => {
return (
<tr key={o.id}>
<td>{o.id}</td>
<td>{o.firstName}</td>
</tr>
);
})}
</table>
</div>
);
}
}
Oh, and one more thing: Use camelCase. Capital letters at the beginning should only be used when declaring classes.