Nested Object Iteration with conditional statements ReactJs - javascript

I have the below api call from React Js. It is a class component. am new to React Js.
class UserComponent extends React.Component {
constructor(props){
super(props)
this.state=({
users: []
});
retrieve(e){
e.preventDefault();
fetch(`http://localhost:8080/test/{this.state.id}`,{
"method":"GET",
"headers":{
"accept":"application/json"
}
})
.then(response => response.json())
.then(response => {
this.setState({
users:response
})
}
render(){
return (
<div>
<form>
<table>
<tbody>
{
this.state.users === '' || null ? '' : Object.entries(this.state.users).map([key,value])=>(
Object.entries(key).map(field=>(
<tr>
<td>
<input name = "firstName" id = firstName" defaultValue = {field.firstName} required/>
</td>
</tr>
))))
}
</tbody>
</table>
</form>
</div>
)
}
}
export default UserComponent
I need to get the firstName and lastName and other details from a simple json file
{
"id":"12224343",
"depId":"1",
"employees":[{
"empId":"1",
"firstName":"sample",
"lastName":"test",
"address":[{
"street":"1",
"pin":"12345"
}]
}]
}
I need to get the firstname and last name of this nested object and address's object's value also. Is there any way like flatmap kind of thing is available in react class component. I didn't use useEffects and all.
when I tried to iterate it am getting "map is not a function. it is undefined"
the class component with state objects have any restrictions. tried a lot of options.
I need to iterate the object of object and fetch only particular values in the UI test box. like first name and lastname. now it is iterating all and displaying all in the UI. which looks messy.
Please help me to get a solution for this.

Since you initialized state users as an array. You can just use users.map(item). Each item contains multiple employees. You can also convert data from the current json file to any data structure which is comfortable for you to display in the table.
const {users} = this.state
users?.map((user,index) => {
return (
<div key={key.id}>
{user?.employees.map(item => {
return (
<div key={item.empId + key.id}>
<input defaultValue = {item.firstName} required/>
</div>
)
})}
</div>
)
} )
I suggest to choose open source UI library such as Material UI or Antd to display data inside table. It saves time to manage responsive web page styles.

I have initialized an empty return object that fixes all the problems and it worked like a charm. please check the below details.
class UserComponent extends React.Component {
constructor(props){
super(props)
this.state=({
users : [{
"id":"",
"depId":"",
"employees":[{
"empId":"",
"firstName":"",
"lastName":"",
"address":[{
"street":"",
"pin":""
}]
}]
}]
});
retrieve(e){
e.preventDefault();
fetch(`http://localhost:8080/test/{this.state.id}`,{
"method":"GET",
"headers":{
"accept":"application/json"
}
})
.then(response => response.json())
.then(response => {
this.setState({
users:response
})
}
render(){
return (
<div>
<form>
<table>
<tbody>
{
this.state.users === '' || null ? '' : Object.entries(this.state.users).map([key,value])=>(
Object.entries(key).map(field=>(
<tr>
<td>
<input name = "firstName" id = firstName" defaultValue = {field.firstName} required/>
</td>
</tr>
))))
}
</tbody>
</table>
</form>
</div>
)
}
}
export default UserComponent

Related

How to change sibling component on event from another component in React JS

I am new to ReactJS and doing some hands on practice. I am working on a page where two tables are present and have linked data. I am looking for some suggestion around on how to communicate dependent data state to multiple elemnts in sibling component
Table 1 is professor table where each professor, subject and its availability is listed.
Table 2 is student table where each student is listed with subject and professor alloted.
I've created tables for each as components and kept them under main component.
import React from 'react'
import Professor from '../Professor';
import Student from '../Student'
function Main() {
return (
<div className="w3-row-padding">
<div className="w3-half"> <Professor /> </div>
<div className="w3-half"> <Student /> </div>
</div>
);
}
Here is sample code for Professor component:
import teachers from "./teachers.json";
class Professor extends Component{
constructor(props) {
super(props);
this.state = {
};
}
renderHeader = () => {
let headerElement = ['name', 'present']
return headerElement.map((key, index) => {
return <th key={index}>{key}</th>
})
}
renderBody = () => {
return teachers && teachers.map(({ StaffID, StaffName, RoleID, Present }) => {
return (
<tr key={StaffID}>
<td>{StaffName}</td>
<td style={{verticalAlign: "middle"}}>
<input type="checkbox" name={StaffName} defaultChecked={Present ? 'checked' : false}
/>
</td>
</tr>
)
})
}
render() {
return (
<div>
<table>
<thead>
<tr>{this.renderHeader()}</tr>
</thead>
<tbody>
{this.renderBody()}
</tbody>
</table>
</div>
)
}
}
export default Professor;
Similarly I have another table for students listing data from student json containing student name and professor id.
In professor table, user can mark presence of professor using checkbox. And on click of checkbox from Professors table, I want to update all rows in Student table with either alternate staff or a message.
In this case, I need to establish sibling component communication and update mulitiple rows in sibling table on an event. Any idea on how this can be achieved will be helpful.
Thanks
You need to lift up your state to the parent component
import { useState } from "React"
import ...
function Main() {
[teacherIsPresent, setTeacherIsPresent] = useState(false)
const toggleTeacherPresence = () => {
setTeacherIsPresent((previousState) => !previousState)
}
return (
<div className="w3-row-padding">
<div className="w3-half"> <Professor toggleTeacherPresence={toggleTeacherPresence} teacherIsPresent={teacherIsPresent}/> </div>
<div className="w3-half"> <Student /> </div>
</div>
);
}
in your child component:
class Professor extends Component{
// your code
<input type="checkbox" name={StaffName} defaultChecked={this.teacherIsPresent ? 'checked' : false} onChange{this.props.toggleTeacherPresence}
/>
// your other code
}
You should consider to use functional components as Childs - since you do it in the parent anyways. This would make things easier...

TypeError: Cannot read property 'characters' of undefined

I have added a method which I want to use on a button.I am trying to delete a field from the table but getting an error. I had initially declared the state with an array.I assume that this error is being generated while setting the state with the filtered array. I am not sure what's wrong here.
Error:
TypeError: Cannot read property 'characters' of undefined
App.js:
import Table from './Table';
import React, { Component } from 'react'
export default class App extends Component {
state={
characters : [
{
name: 'Smit',
job: 'Theme Specialist',
},
{
name: 'Charlie',
job: 'Janitor',
},
{
name: 'Mac',
job: 'Bouncer',
},
{
name: 'Dee',
job: 'Aspring actress',
},
{
name: 'Dennis',
job: 'Bartender',
},
]
}
removeElement(index){
this.setState({
characters: this.state.characters.filter(i=>{
return i !== index
})
})
console.log("remove"+this.state.characters)
}
render() {
return (
<div>
<Table data={this.state.characters} deleted={this.removeElement}/>
</div>
)
}
}
Table.js:
import React, { Component } from 'react'
function TableHeader() {
return (
<thead>
<tr>
<th>Name</th>
<th>Job</th>
</tr>
</thead>
)
}
function TableBody(props) {
const dataBody= props.data.map( (index)=>{
return(
<tr key={index.data}>
<td>
{index.name}
</td>
<td>
{index.job}
</td>
<button onClick={()=>
props.deleted(index)
}>
Delete
</button>
</tr>
)
})
return(
<tbody>
{dataBody}
</tbody>
)
}
export default class Table extends Component {
render() {
return (
<table>
<TableHeader/>
<TableBody data={this.props.data} deleted={this.props.deleted}/>
</table>
)
}
}
That’s a typical react this pitfall. When you pass a method of parent class to child components as prop, most of the time, you want its this bound.
<Table
data={this.state.characters}
deleted={this.removeElement.bind(this)}
/>
Better, you can use arrow function syntax to declare the method in the first place, so you don’t need to worry about the this problem later on.
removeElement = (index) => { ... }
Now .bind(this) is no longer necessary.
use arrow function as class method:
removeElement = (index)=>{
this.setState({
characters: this.state.characters.filter(i=>{
return i !== index
})
})
console.log("remove"+this.state.characters)
}

React- input field turns to string after onChange

I have a small app with three components
parent => App
Son => Courses
Grandchild => Course
The app state lies in the App component, in the Course component I have an input field with an onChange event that spouse to change the state in the app component, the problem is that every time I type the input changes to a string and I can't keep typing and changing the state.
The values arrive to the parent.
This is my Course code
import React, { Component } from 'react'
class Course extends Component {
updatedGrade = (e) => {
this.props.updateCourseGrade(Number(e.target.value), Number(e.target.id));
};
render() {
const {id, courseType, courseName, courseGrade} = this.props.course;
return (
<tr key={id}>
<td>
{courseName}
</td>
<td>
{(courseType ? 'a' : 'm' )}
</td>
<td>
{(courseGrade !== ''
? courseGrade
: <input
type="number"
id={id}
onChange={this.updatedGrade}
value={courseGrade}
/>
)}
</td>
<td>
<button
onClick={this.props.removeCourse.bind(this, id)}
style={btnStyle}
>
remove
</button>
</td>
</tr>
)
}
}
this is my App relevant code:
class App extends Component {
state = {
courses: [
{
id: 1,
courseName: 'bioliogy,
courseType: false,
courseHours: 10,
courseGrade: ''
},{
id: 2,
courseName: 'Mahematics,
courseType: true,
courseHours: 20,
courseGrade: ''
},{
id: 3,
courseName: 'History,
courseType: false,
courseHours: 30,
courseGrade: 50
}
]
};
updateCourseGrade(courseGrade, id){
//const courseGradeNum = Number(courseGrade);
this.setState({
courses: this.state.courses.map(course => course.id === id ? {...course, courseGrade } : course)
})
console.log('courseGrade ', courseGrade);
Now, when I do this:
updateCourseGrade(courseGrade, id){
const courseGradeNum = Number(courseGrade);
this.setState({
courses: this.state.courses.map(course => course.id === id ? {...course, courseGradeNum } : course)
})
The state will get a new value while typing named courseGrade and I don't want this.
as well the courseGrade is already defined as a Number in the Course component
What can I do? maybe I shouldn't use value in the course component?
UPDATE
According to Freeman Lambda request, this is the state after I change the value in the input field,
the state of courseGrade of the desired course changes. but because the input field disappears I cannot keep typing.
Link to a video that shows what happens
https://www.screencast.com/t/Cyz1v6zMWsq
Here:
{(courseGrade !== ''
? courseGrade
: <input
type="number"
id={id}
onChange={this.updatedGrade}
value={courseGrade}
/>
)}
You explicitely change the input to a plain string a soon as courseGrade is !== ''
if you want to be able to keep typing you have to stick with an input during typing. If you want the input to disapear after typing you will have to add a button controlling a state that removes the input, for example:
class Course extends Component {
state = {
gradeValidated: false,
}
updatedGrade = (e) => {
this.props.updateCourseGrade(Number(e.target.value), Number(e.target.id));
};
toggleGradeInput = (e) => {
this.setState((state) => ({ gradeValidated: !state.gradeValidated }));
};
render() {
const {id, courseType, courseName, courseGrade} = this.props.course;
return (
<tr key={id}>
<td>
{courseName}
</td>
<td>
{(courseType ? 'a' : 'm' )}
</td>
<td>
{(this.state.gradeValidated
? courseGrade
: <input
type="number"
id={id}
onChange={this.updatedGrade}
value={courseGrade}
/>
)}
</td>
<td>
<button
onClick={this.toggleGradeInput}
style={btnStyle}
>
toggle input
</button>
<button
onClick={this.props.removeCourse.bind(this, id)}
style={btnStyle}
>
remove
</button>
</td>
</tr>
)
}
}
The problem is here
courseGrade !== ''
? courseGrade
: <input
type="number"
id={id}
onChange={this.updatedGrade}
value={courseGrade}
/>
the first condition is set to true if you set any state instead of '', so my idea is to use a save button for the grate and onChange keep on a local state.
You should use an updater function for this, I think what is happening is that state updates are getting batched.
So instead of
this.setState({
courses: this.state.courses.map(course => course.id === id ? {...course, courseGradeNum } : course)
})
try
this.setState( prevState => ({
courses: prevState.courses.map(course => course.id === id ? {...course, courseGradeNum } : course)
}))
If this wasn't the problem can you create a code sandbox because there just isn't enough code to understand what is going on. For example, updateCourseGrade is being bound anywhere. don't know if you haven't or if you are just not showing it.

Filtering data in table in React based on values of radio buttons and input field after hitting submit failed

I have data from local json file that I use to create a table.
In Table class component I have the table contains top 10 movies. The data is being displayed from filteredData state variable and are well displayed after loading the table. Above table I have 2 radio buttons, to choose whether I want to search data based on column title or column genre saved in state variable radioSearch by using function searchHandler. Then I have an input field, when I enter a string in it the result is being saved in searchFieldInput state variable, by using updatedSearch function.
Finally, I have submitHandler function in this component to filter the table based on selected radio button(title/genre of the film), and after that based on entered string in input field. The filtered data I am putting into filteredData variable in order to update the state by using setState. Unfortunately no filtering is being done after hitting submit. In Table component is nested TableRow component which should display the data based on applied filtering. I don't know whether the concept of submitHandler function is wrong, and why is not filtering the data? Can somebody help.
Here is my Table component:
import React, {Component} from 'react';
import TableRow from './TableRow/TableRow';
class Table extends Component {
constructor(props) {
super(props)
this.state = {
filteredData: this.props.data,
searchFieldInput: '',
radioSearch: this.props.radioSearch,
transformed: false
}
}
updatedSearch = (event) => {
this.setState({
searchFieldInput: event.target.value
})
}
searchHandler = (e) => {
this.setState({
radioSearch: e.target.value
})
};
submitHandler = (event) => {
event.preventDefault();
if(this.state.radioSearch === "title") {
let filteredData = this.props.data.filter(column => {
return column.title.toLowerCase().indexOf(this.state.searchFieldInput.toLowerCase()) !== -1;
});
this.setState({
filteredData: filteredData
});
return this.state.filteredData;
} else if(this.state.radioSearch === "genre"){
let filteredData = this.props.data.filter(column => {
return column.genre.toLowerCase().indexOf(this.state.searchFieldInput.toLowerCase()) !== -1;
});
this.setState({
filteredData: filteredData
});
return this.state.filteredData;
}
console.log(this.state.radioSearch);
}
render() {
let filteredData = this.props.data.filter(column => {
return column.title.toLowerCase().indexOf(this.state.searchFieldInput.toLowerCase()) !== -1;
});
return(
<React.Fragment>
<div className="container-fluid">
<div className="container">
<form>
{/*Search field*/}
<input
className={"Search" + (this.state.transformed === true ?
' transformed' : '')}
type="text"
placeholder={(this.state.transformed === true ?
'' : 'Type here')}
maxLength="20"
value={this.state.searchFieldInput} required
onChange={this.updatedSearch.bind(this)}
/>
<button type="submit">
Search
</button>
{/*Radio buttons*/}
<label htmlFor="title">
<input type="radio" name="title" id="title" value="title" checked={this.state.radioSearch === "title"}
onChange={this.searchHandler}/>
title
</label>
<label htmlFor="genre">
<input type="radio" name="genre" id="genre" value="genre" checked={this.state.radioSearch === "genre"}
onChange={this.searchHandler}/>
genre
</label>
</form>
</div>
<div className="container">
<table>
<thead>
<tr>
<th>No.</th>
<th>Picture</th>
<th>Release date</th>
<th>Genre</th>
<th>Rating</th>
</tr>
</thead>
<tbody>
{this.state.filteredData.map((row, index) => {
return (
<TableRow
numeration={index + 1}
key={row.id}
row={row}
/>
)
})
}
</tbody>
</table>
</div>
</div>
</React.Fragment>
)
}
}
export default Table;
I think its because you forgot to add the function to the submit button:
<button type="submit" onSubmit={this.submitHandler.bind(this)}>
Search
</button>

Use options to filter data inside of a table with React

So basically I'm working with a large dataset that gives you a list of videos (about 100-200) for each day in this year (2018, so the data is from January 1st, 2018 to February 18th, 2018). What I want to do is use a table to show all of the videos in ONE day. I'm using the <option> tag for the list of all the dates. I'm currently stuck because I'm trying to make it where you click on a certain date, a table will generate and show the list of videos from that day with the video name and the channel that posted the video. Here is a copy of my code:
The code below is to generate each row in the table:
// Simple TableRow component for showing a <tr>
class TableRow extends Component {
render() {
return (
<tr>
<td>
{ this.props.date }
</td>
<td>
{ this.props.title }
</td>
<td>
{ this.props.channel }
</td>
<td>
{ this.props.view }
</td>
<td>
<img src={ this.props.photo }></img>
</td>
</tr>
)
}
}
The code below is to generate the whole table and calls on the TableRow
// Class for a table
class Table extends Component {
constructor(props) {
super(props);
}
// Should return a TableRow component for each element in this.props.data
render() {
return (
<div>
<table className="table">
<tbody>
<tr>
<th>Date</th>
<th>Video Name</th>
<th>Channel</th>
<th>Views</th>
<th>Photo</th>
</tr>
{ this.props.songs.map(function(d, i) {
return <TableRow key={ 'song-' + i } date={ d.trending_date } title={ d.title } channel={ d.channel_title } view={d.views} photo={ d.thumbnail_link } />
}) }
</tbody>
</table>
</div>
)
}
}
Finally, this is where I'm doing the bulk of the work. This function is generating the options and creating the table based on the specific date. this.props.data is the dataset passed into the Options class from a different class
class Options extends Component {
constructor(props) {
super(props);
this.state = {
value: '18.01.01',
data: [],
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
value: event.target.value,
});
}
render() {
// Gets the trending dates but ignores duplicates
let dateData = d3.nest()
.key((d) => { return d.trending_date })
.entries(this.props.data);
return (
<div>
Choose a Date:
<select value={this.state.value} onChange={this.handleChange}>
{ dateData.map((d) => {
return <option value={d.key}>{d.key}</option>
}) }
</select>
<Table songs={this.state.data} />
</div>
);
}
}
I'm trying to use this.state.value to get the VALUE in the object, dateData, since dateData contains all of the videos for each date. However, I can't seem to find a way to set this.state.data to the correct dataset to pass into the Table class... Does anybody have any ideas how I can approach this problem?
Example of how the dataset looks like: https://i.imgur.com/S5JIkbx.png

Categories

Resources