Getting Error TypeError: Cannot read property 'rows' of null - reactjs

I am fairly new to react and I am trying add a row in my react app on the click of a button. I followed this link How to add and remove table rows Dynamically in React.js
to do so but I am not able to translate it to my code.
My code here:
KPIDetails.js
Here I am rendering the view in KPI Details.js file.
<MuiThemeProvider>
<React.Fragment>
<Grid container>
<Grid item xs={6} direction="row" alignItems="center">
<table
className="table table-bordered table-hover"
id="tab_logic"
>
<thead>
<tr>
<th className="text-center"> KPI</th>
<th className="text-center"> UOM</th>
<th className="text-center"> Base</th>
<th className="text-center"> Target</th>
<th className="text-center"> Target Date</th>
</tr>
</thead>
<tbody>
{this.state.rows.map((item, idx) => (
<tr id="addr0" key={idx}>
<td>{idx}</td>
<td>
<input
type="text"
name="Kpi_Before"
value={this.state.rows[idx].Kpi_Before}
onChange={this.handleChangeRows(idx)}
className="form-control"
/>
</td>
<td>
<input
type="text"
name="UOM_Before"
value={this.state.rows[idx].UOM_Before}
onChange={this.handleChangeRows(idx)}
className="form-control"
/>
</td>
<td>
<input
type="text"
name="Base_Before"
value={this.state.rows[idx].Base_Before}
onChange={this.handleChangeRows(idx)}
className="form-control"
/>
</td>
<td>
<input
type="text"
name="Target_Before"
value={this.state.rows[idx].Target_Before}
onChange={this.handleChangeRows(idx)}
className="form-control"
/>
</td>
<td>
<input
type="text"
name="Target_Before"
value={this.state.rows[idx].dateTime}
onChange={this.handleChangeRows(idx)}
className="form-control"
/>
</td>
</tr>
))}
</tbody>
</table>
<button
onClick={this.handleRemoveRow}
className="pull-right btn btn-default"
>
Delete Row
</button>
<Button
variant="outlined"
color="primary"
onClick={this.handleAddRow}
size="small"
style={styles.button}
>
+
</Button>
</Grid>
</Grid>
< /React.Fragment>
</MuiThemeProvider>
This is the js file where I call all the functions
User Form.js
export class UserForm extends Component {
state = {
step: 1,
Title: "",
Details: "",
What: "",
Why: "",
How: "",
Status: "",
Cost: "",
Benefits: "",
Kpi_Before: "",
Kpi_After: "",
Time: "",
UOM_Before: "",
Base_Before: "",
Target_Before: "",
dateTime: null,
rows: ["row1"]
};
//1
handleChangeRows = idx => e => {
const {Kpi_Before, value} = e.target;
const rows = [...this.state.rows];
rows[idx] = {
[Kpi_Before]: value
};
this.setState({
rows
});
};
//2
handleAddRow = () => {
const item = {
KPI_Before: "",
UOM_Before: "",
Base_Before: "",
Target_Before: "",
dateTime: ""
};
this.setState({
rows: [...this.state.rows, item]
});
};
//3
handleRemoveRow = () => {
this.setState({
rows: this.state.rows.slice(0, -1)
});
};
}
What am I doing wrong. Is there any other way to do it?

I have added:
state = {
rows: []
};
in the component and it solved the issue

Related

Message prints in every Dynamic Accordion in ReactJs

I have a dynamic Accordion in ReactJs. I am getting the message from my backend. but it's printing in every Accordion. I'm sharing the code
import React, { useState, useEffect } from "react";
import ApplicantDash from "./ApplicantDash";
import {
Accordion,
AccordionSummary,
AccordionDetails,
Typography,
} from "#material-ui/core";
import * as FcIcons from "react-icons/fc";
import ApplicantService from "../services/ApplicantService";
export default function AvailJobs() {
const [aplcntEmail, setAplcntEmail] = useState("aman#gmail.com"); //change to aplcntemail
const [isShow, setIsShow] = useState(false);
const [msg, setMsg] = useState([""]);
const [job, setJob] = useState([
{
jobTitle: "",
dateOfPosting: Date,
lastDateToApply: new Date().toLocaleDateString([], {
year: "numeric",
month: "long",
day: "numeric",
}),
preferableSkills: [],
requiredExp: 0,
recruiterEmail: "",
companyName: "",
companyAddress: "",
},
]);
useEffect(() => {
const data = ApplicantService.getAllJobs()
.then((response) => {
console.log(response.data);
setJob(response.data);
})
.catch((error) => {
alert(error.response.data);
});
}, []);
const onApplyButton = (item,key) => {
const data2 = ApplicantService.applyForJob(aplcntEmail, item)
.then((response) => {
console.log(response.data);
setIsShow(true);
setMsg(response.data)
})
.catch((error) => {
setIsShow(true);
setMsg(error.response.data);
});
};
return (
<div>
<ApplicantDash />
<div className="container bg-light">
<div className="card-bodies">
<section className="mb-4">
<h2 className="h1-responsive font-weight-bold text-center my-4">
All Available jobs
</h2>
</section>
{job.map((item, key) => (
<>
<Accordion key={key}>
<AccordionSummary
expandIcon={<FcIcons.FcExpand />}
aria-controls="panel1a-content"
id="panel1a-header"
className="Accordian"
>
<Typography>
<div className="d-flex p-1 justify-content-evenly">
<div className="p-1">
<b> Job: </b> {item.jobTitle}
</div>
<div className="p-2"></div>
<div className="p-1">
<b> Company: </b> {item.companyName}
</div>
<div className="p-2"></div>
<div className="p-1">
<b> Last Date: </b> {item.lastDateToApply}
</div>
</div>
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
<div className="container">
<table class="table table-borderless">
<tbody>
<tr>
<td>JOB TITLE</td>
<td>:</td>
<td>
<b>{item.jobTitle}</b>
</td>
</tr>
<tr>
<td>Company</td>
<td>:</td>
<td>
<b>{item.companyName}</b>
</td>
</tr>
<tr>
<td>Address</td>
<td>:</td>
<td>
<b>{item.companyAddress}</b>
</td>
</tr>
<tr>
<td>Last Date to Apply</td>
<td>:</td>
<td>
<b>{item.lastDateToApply}</b>
</td>
</tr>
<tr>
<td>Experience</td>
<td>:</td>
<td>
<b>{item.requiredExp}</b>
</td>
</tr>
<tr>
<td> Skills </td>
<td>:</td>
<td>
<table className="table table-condensed w-auto table-borderless table-hover">
{item.preferableSkills.map((S, index1) => {
return (
<tbody key={index1}>
<td scope="col">
{index1 + 1}.<b>{S}</b>
</td>
</tbody>
);
})}
</table>
</td>
</tr>
<tr>
<td></td>
<td></td>
<td>
<button
type="button"
class="btn btn-primary"
onClick={() => onApplyButton(item,key)}
>
Apply for the job{" "}
</button>
</td>
</tr>
</tbody>
{isShow && <>
{msg}
</>}
</table>
</div>
</Typography>
</AccordionDetails>
</Accordion>
</>
))}
</div>
</div>
</div>
);
}
Now when I click on Apply for this job button. The message I get from backend prints only to Active accordion
Here some pictures which might help.
enter image description here
As you can see the response from backend is prints in the both of the accordion
Issue
The issue here is that you've a single boolean isShow state and a single msg state, and all the accordion detail sections use the same single isShow state to conditionally render the msg state.
Solution
A simple solution would be to store the id, or title, or index, of the accordion to show the message of.
Example:
export default function AvailJobs() {
...
const [isShow, setIsShow] = useState({}); // <-- initially empty object
...
const onApplyButton = (item, key) => {
ApplicantService.applyForJob(aplcntEmail, item)
.then((response) => {
console.log(response.data);
setMsg(response.data);
})
.catch((error) => {
setMsg(error.response.data);
})
.finally(() => {
setIsShow(show => ({
...show,
[key]: true // <-- set true the specific key
}));
});
};
return (
<div>
...
{job.map((item, key) => (
<Accordion key={key}>
...
<AccordionDetails>
<Typography>
<div className="container">
<table class="table table-borderless">
<tbody>
...
<tr>
...
<td>
<button
type="button"
class="btn btn-primary"
onClick={() => onApplyButton(item, key)}
>
Apply for the job
</button>
</td>
</tr>
</tbody>
{isShow[key] && <>{msg}</>} // <-- check if isShow[key] is truthy
</table>
</div>
</Typography>
</AccordionDetails>
</Accordion>
))}
...
</div>
);
}

how search from array of object? and sort data by ascending and descending by click on column name?

I am beginner in Reactjs and I make array and create table from array of objects Now I want to perform search operation on created table. I tried lot but i cannot get what I should write in updateSearch() function to search from table and display search results and also sort data by ascending and descending by click on column name. so, plz help me to sort out this problem
class Hello extends Component {
constructor(props) {
super(props)
this.state = {
search: '',
Data:[
{
id: 1,
fullName: 'abc',
email:'example#gmail.com',
},
{
id: 2,
fullName: 'qps',
email:'qps#gmail.com',
},
{
id: 3,
fullName: 'qwe',
email:'qwe#gmail.com',
},
]
}
}
updateSearch(event){
this.setState({
search : event.target.value
});
console.log(event.target.value);
}
render() {
return (
<div>
<h1>welcome to React</h1>
<input type="text" placeholder="Enter item to be searched" value={this.state.search} onChange={this.updateSearch.bind(this)} />
<table className="table table-hover table-dark">
<tbody>
<tr>
<th>ID</th>
<th>Full Name</th>
<th>Email</th>
</tr>
{
this.state.Data.map((item,index)=>(
<tr key={item.id}>
<td >{item.id}</td>
<td >{item.fullName}</td>
<td>{item.email}</td>
</tr>
))
}
</tbody>
</table>
</div>
)
}
}
export default Hello
You can filter items in the Data.map, for example:
{ this.state.Data.filter((item) => {
return !(this.state.search) // if search is not set, return all items
|| item.fullName.match(RegExp(search, 'i'))
|| item.email.match(RegExp(search, 'i'))
}).map((item,index)=>(
<tr key={item.id}>
<td >{item.id}</td>
<td >{item.fullName}</td>
<td>{item.email}</td>
</tr>
)}
Working Code
class Hello extends React.Component {
constructor(props) {
super(props)
this.state = {
search: '',
Data: [{
id: 1,
fullName: 'abc',
email: 'example#gmail.com',
},
{
id: 2,
fullName: 'qps',
email: 'qps#gmail.com',
},
{
id: 3,
fullName: 'qwe',
email: 'qwe#gmail.com',
},
]
}
}
updateSearch(event) {
this.setState({
search: event.target.value
});
}
render() {
return (
<div>
<h1>welcome to React</h1>
<input type="text" placeholder="Enter item to be searched" value={this.state.search} onChange={this.updateSearch.bind(this)} />
<table className="table table-hover table-dark">
<tbody>
<tr>
<th>ID</th>
<th>Full Name</th>
<th>Email</th>
</tr>
{
this.state.Data.filter((item) => {
return !(this.state.search) // if search is not set, return all items
|| item.fullName.match(RegExp(this.state.search, 'i'))
|| item.email.match(RegExp(this.state.search, 'i'))
}).map((item,index)=>(
<tr key={item.id}>
<td >{item.id}</td>
<td >{item.fullName}</td>
<td>{item.email}</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
}
ReactDOM.render( < Hello / > , document.getElementById('root'));
<script src="https://unpkg.com/react#16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" crossorigin></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<div id="root">
</div>
Probably you are trying to have a filter functionality in your component
class App extends Component {
Data = [
{
id: 1,
fullName: 'abc',
email: 'example#gmail.com',
},
{
id: 2,
fullName: 'qps',
email: 'qps#gmail.com',
},
{
id: 3,
fullName: 'qwe',
email: 'qwe#gmail.com',
},
];
constructor(props) {
super(props)
this.state = {
search: '',
filteredArray: this.Data,
}
}
updateSearch(event) {
this.setState({
search: event.target.value,
filteredArray: this.Data.filter((data) => {
return data.fullName.includes(event.target.value);
})
});
console.log(event.target.value);
}
render() {
return (
<div>
<h1>welcome to React</h1>
<input type="text" placeholder="Enter item to be searched" value={this.state.search} onChange={this.updateSearch.bind(this)} />
<table className="table table-hover table-dark">
<tbody>
<tr>
<th>ID</th>
<th>Full Name</th>
<th>Email</th>
</tr>
{
this.state.filteredArray.map((item, index) => (
<tr key={item.id}>
<td >{item.id}</td>
<td >{item.fullName}</td>
<td>{item.email}</td>
</tr>
))
}
</tbody>
</table>
</div>
)
}
}

error: cannot read <a> property of 'undefined' but the 'undefined' object appears to be valid in another code segment

I have a state 'raterNameList', which is an array of objects with different properties (rater1name, rater1score, rater2name, rater2score, ...)
when I called this.state.raterNameList[0].rater1name, I got the error "TypeError: Cannot read property 'rater1name' of undefined", and if I put in the condition check for this.state.raterNameList[0]!=null, it appears that raterNameList[0] is null.
However, within the next code section, I called this.state.raterNameList.map and display rater1score for every item in the list, it can display the value for item #0 just fine.
I am very new to react and don't know what I did wrong.
import React, { Component } from 'react'
import '../App.css'
class Consensus extends Component {
constructor() {
super();
this.state = {
justLoaded:true,
panelval: '',
hr:'',
raterNameList: [],
r1: '',
r2:'',
r3:'',
};
this.handleChange = this.handleChange.bind(this);
this.handleChangeHR = this.handleChangeHR.bind(this);
this.handleChangeFinalScore = this.handleChangeFinalScore.bind(this);
this.handleClick = this.handleClick.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
getRaterList = () => {
var fetchquery="./api/consensustable?panelName=" + this.state.panelval
console.log ("Hello" + fetchquery)
console.log ("mounted =" + this.state.justLoaded)
fetch(fetchquery)
.then(res => res.json())
.then (raterNameList =>{
this.setState({raterNameList})
})
};
getRaterName =()=>{
if (this.state.raterNameList[0]!=null){
this.setState({r1: this.state.raterNameList[0].rater1name});
this.setState({r2: this.state.raterNameList[0].rater2name});
this.setState({r3: this.state.raterNameList[0].rater3name});
}
else{
this.setState({r1: "Rater1"});
this.setState({r2: "Rater2"});
this.setState({r3: "Rater3"});
}
}
handleChange(event) {
this.setState({panelval: event.target.value});
}
handleChangeHR(event) {
this.setState({hr: event.target.value});
}
handleChangeFinalScore(event) {
this.setState({finalScore: event.target.value});
}
handleClick(event) {
console.log('A name was submitted: ' + this.state.panelval);
this.setState({justLoaded: false});
event.preventDefault();
this.getRaterList();
this.getRaterName();
}
handleSubmit(event) {
event.preventDefault();
}
render () {
if (!this.state.justLoaded)
{
return( <form onSubmit={this.handleSubmit}>
<label> Consensus Panel Name: {this.state.panelval} </label>
<table>
<tbody >
<tr>
<table>
<tbody>
<td className="matchsizing">Applicant Name</td>
<td className="scorebox">{this.state.r1}</td>
<td className="scorebox">{this.state.r2}</td>
<td className="scorebox">{this.state.r3}</td>
<td className="finalscorebox">Final Score</td>
<td className="finalscorebox">Advance?</td>
</tbody>
</table>
</tr>
<tr>
<table >
{this.state.raterNameList.map((ra, i)=>(
<tbody>
<tr>
<td className="matchsizing" >{i}: {ra.applicantname} </td>
<td className="scorebox" >{ra.rater1score}</td>
<td className="scorebox" >{ra.rater2score}</td>
<td className="scorebox">{ra.rater3score}</td>
<td className="scorebox">
<input type="text" name="finalScore" value={ra.finalscore} />
</td>
<td className="scorebox">ra.advance</td>
</tr>
<tr>
<td colspan="6">
<textarea rows ="5" cols="136" className="textarea" placeholder="Enter details here..."></textarea>
</td>
</tr>
</tbody>
)
)
}
</table>
</tr>
<tr>
<td colspan="9">
<label>Human Resources Representative: </label>
<input type="text" value={this.state.hr} onChange={this.handleChangeHR} />
</td>
</tr>
</tbody>
</table>
<input type="submit" value="Save" />
</form>
)
}
else
{
return(
<form onSubmit={this.handleSubmit}>
<label>Panel Name: </label>
<input type="text" value={this.state.panelval} onChange={this.handleChange} />
<button type="button" value="get Panel" onClick={this.handleClick}> get Panel </button>
</form>
)
}
}}
export default Consensus;
This may work for you:
import React, { Component } from "react";
import "../App.css";
class Consensus extends Component {
constructor() {
super();
this.state = {
justLoaded: true,
panelval: "",
hr: "",
raterNameList: [],
r1: "",
r2: "",
r3: ""
};
this.handleChange = this.handleChange.bind(this);
this.handleChangeHR = this.handleChangeHR.bind(this);
this.handleChangeFinalScore = this.handleChangeFinalScore.bind(this);
this.handleClick = this.handleClick.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
getRaterList = cb => {
var fetchquery = "./api/consensustable?panelName=" + this.state.panelval;
console.log("Hello" + fetchquery);
console.log("mounted =" + this.state.justLoaded);
fetch(fetchquery)
.then(res => res.json())
.then(raterNameList => {
this.setState({ raterNameList });
cb(raterNameList);
});
};
getRaterName = raterNameList => {
if (raterNameList && raterNameList.length) {
this.setState({
r1: raterNameList[0].rater1name,
r2: raterNameList[0].rater2name,
r3: raterNameList[0].rater3name
});
} else {
this.setState({ r1: "Rater1", r2: "Rater2", r3: "Rater3" });
}
};
handleChange(event) {
this.setState({ panelval: event.target.value });
}
handleChangeHR(event) {
this.setState({ hr: event.target.value });
}
handleChangeFinalScore(event) {
this.setState({ finalScore: event.target.value });
}
handleClick(event) {
event.preventDefault();
console.log("A name was submitted: " + this.state.panelval);
this.setState({ justLoaded: false });
this.getRaterList(raterNameList => {
this.getRaterName(raterNameList);
});
}
handleSubmit(event) {
event.preventDefault();
}
render() {
if (!this.state.justLoaded) {
return (
<form onSubmit={this.handleSubmit}>
<label> Consensus Panel Name: {this.state.panelval} </label>
<table>
<tbody>
<tr>
<table>
<tbody>
<td className="matchsizing">Applicant Name</td>
<td className="scorebox">{this.state.r1}</td>
<td className="scorebox">{this.state.r2}</td>
<td className="scorebox">{this.state.r3}</td>
<td className="finalscorebox">Final Score</td>
<td className="finalscorebox">Advance?</td>
</tbody>
</table>
</tr>
<tr>
<table>
{this.state.raterNameList.map((ra, i) => (
<tbody>
<tr>
<td className="matchsizing">
{i}: {ra.applicantname}{" "}
</td>
<td className="scorebox">{ra.rater1score}</td>
<td className="scorebox">{ra.rater2score}</td>
<td className="scorebox">{ra.rater3score}</td>
<td className="scorebox">
<input
type="text"
name="finalScore"
value={ra.finalscore}
/>
</td>
<td className="scorebox">ra.advance</td>
</tr>
<tr>
<td colspan="6">
<textarea
rows="5"
cols="136"
className="textarea"
placeholder="Enter details here..."
/>
</td>
</tr>
</tbody>
))}
</table>
</tr>
<tr>
<td colspan="9">
<label>Human Resources Representative: </label>
<input
type="text"
value={this.state.hr}
onChange={this.handleChangeHR}
/>
</td>
</tr>
</tbody>
</table>
<input type="submit" value="Save" />
</form>
);
} else {
return (
<form onSubmit={this.handleSubmit}>
<label>Panel Name: </label>
<input
type="text"
value={this.state.panelval}
onChange={this.handleChange}
/>
<button type="button" value="get Panel" onClick={this.handleClick}>
{" "}
get Panel{" "}
</button>
</form>
);
}
}
}
export default Consensus;
handleClick(event) {
event.preventDefault();
console.log("A name was submitted: " + this.state.panelval);
this.setState({ justLoaded: false });
this.getRaterList() // <- here we made async call
this.getRaterName() // <- this function will called before we get back data
}
Explanation
You were making async call and before that response comes back you started reading state and that is not yet updated because you api call is in progress state.
What i did is added one callback function and once app call is done we will set state and call callback function so that getRaterName state will be called once we got the data.
also i've just refined
getRaterName we don't need to call setState function for each object we can update state in one shot.
you can refine other setState same way.
Also instead of setting raterName after setting raterNameList you can do this as well
getRaterList = () => {
var fetchquery="./api/consensustable?panelName=" + this.state.panelval
console.log ("Hello" + fetchquery)
console.log ("mounted =" + this.state.justLoaded)
fetch(fetchquery)
.then(res => res.json())
.then (raterNameList =>{
this.setState({
raterNameList,
r1: raterNameList[0] ? raterNameList[0].rater1name: "Rater1",
r2: raterNameList[0] ? raterNameList[0].rater2name: "Rater2",
r3: raterNameList[0] ? raterNameList[0].rater3name: "Rater3"
})
})
};
handleClick(event) {
event.preventDefault();
console.log("A name was submitted: " + this.state.panelval);
this.setState({ justLoaded: false });
this.getRaterList();
}

Add row to existing table dynamically in ReactJS

I am learning ReactJS.
I have pre-existing table rendered which contains thead and only 1 row by default. Now on button click, I want to append a row everytime the button click, but the max rows appended should not be greater than 4.
Here is my code:
import React, { Component } from "react";
import Sidebar from "../Home/Sidebar";
import axios from "axios";
import $ from "jquery";
import { isDivisibleBy100 } from "../utils/utility";
import { Chart } from "react-charts";
class Strategy extends Component {
state = {
Price: [],
chart_data: [],
loadData: true,
unit: parseFloat(0),
loadUnit: true,
};
componentDidMount() {
this.getPriceList();
}
getPriceList() {
axios.get("http://localhost:8000/listprice/").then(res => {
if (res.data.result === 1) {
this.setState({ Price: res.data.data });
}
});
}
appendRow(event) {
var rel = event.target.getAttribute("rel");
rel = parseInt(rel) + 1;
console.log(rel);
var addRow = (
<tr>
<td>
<input type="text" id={`select-type` + rel} />
</td>
<td>
<input type="text" id={`select-position` + rel} />
</td>
</tr>
);
$(".table tbody").append(appRow);
}
render() {
return (
<div className="container container_padding">
<div className="row">
<Sidebar />
<div className="col-md-9 col-sm-9 col-xs-12 white-box">
<div className="col-sm-12">
<h3 className="col-sm-4" style={{ padding: "0px" }}>
Strategy Plan:
</h3>
<div className="col-sm-7" />
<div className="col-sm-1" style={{ marginTop: "15px" }}>
<button
rel="1"
type="button"
id="addbtn"
className="btn btn-circle"
onClick={this.appendRow}
>
<i className="fa fa-plus" />
</button>
</div>
</div>
<div className="col-sm-12 a">
<div className="table-responsive">
<table className="table table-bordered">
<thead>
<tr>
<td>#</td>
<td>Type</td>
<td>Position</td>
<td>Price</td>
<td>Number</td>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>
<select
className="form-control"
name="select-type"
id="select-type"
>
<option value="select">Select</option>
<option value="one">1</option>
<option value="two">2</option>
</select>
</td>
<td>
<select
className="form-control"
name="select-position"
id="select-position"
>
<option value="select">Select</option>
<option value="a">A</option>
<option value="b">B</option>
</select>
</td>
<td>
<select
className="form-control"
name="price-list"
id="price-list"
onChange={event =>
this.handlePriceChange(event)
}
>
<option value="select">Select</option>
{this.state.Price.map(p => (
<option
value={p.pprice}
key={p.price}
>
{p.price}
</option>
))}
</select>
</td>
<td style={{ width: "180px" }}>
<input
id="input-number"
type="text"
className="form-control"
defaultValue="1"
/>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div className="col-sm-12">
<button
className="btn"
onClick={() => this.handleClick()}
>
Calculate
</button>
</div>
{this.state.loadData ? (
""
) : (
<div
style={{
width: "600px",
height: "300px",
marginTop: "35px",
marginLeft: "25px",
marginBottom: "10px"
}}
>
<Chart
data={this.state.chart_data}
series={{ type: "line" }}
axes={[
{ primary: true, type: "linear", position: "bottom" },
{ type: "linear", position: "left" }
]}
primaryCursor
secondaryCursor
tooltip
/>
</div>
)}
</div>
</div>
</div>
);
}
}
export default Strategy;
The appendRow function is not appending the row.
What am I missing? Is there any better way to achieve this?
Please suggest.
Thanks in advance
You are using jquery and directly handling real DOM. With React we use Virtual DOM and don't manipulate the real DOM. Unlike Jquery, in react you don't have to worry about handling UI. Your concern should be handling the data properly, leave the UI updates to React. You haven't provided the Table Component information here. So, I would give you a code sample which does exactly what you want to achieve. For the button you can place it where it's needed within this component.
import React from "react";
class Table extends React.Component {
state = {
data: []
};
appendChild = () => {
let { data } = this.state;
data.push(data.length); // data.length is one more than actual length since array starts from 0.
// Every time you call append row it adds new element to this array.
// You can also add objects here and use that to create row if you want.
this.setState({data});
};
render() {
return (
<table>
<thead>
<th>Type</th>
<th>Position</th>
</thead>
<tbody>
{this.state.data.map(id => (
<Row id = {id} />
))}
</tbody>
</table>
);
}
}
const Row = ({ id }) => (
<tr>
<td>
<input type="text" id={`select-type-${id}`} />
</td>
<td>
<input type="text" id={`select-position-${id}`} />
</td>
</tr>
);
Constructor method:
constructor(props) {
super(props)
this.state = {rows: []};
}
Inside your appendRow method, instead of adding the tr directly to your tbody, add the tr to the rows state:
appendRow(event) {
var rel = event.target.getAttribute("rel");
rel = parseInt(rel) + 1;
var joined = this.state.rows.concat(
<tr>
<td>
<input type="text" id={`select-type` + rel} />
</td>
<td>
<input type="text" id={`select-position` + rel} />
</td>
</tr>
);
this.setState({ rows: joined })
}
Inside your render method, just put the array in. It will be re-rendered whenever setState is called:
render() {
...
return (
...
<button
rel="1"
type="button"
id="addbtn"
className="btn btn-circle"
onClick={this.appendRow}>
<i className="fa fa-plus" />
</button>
...
<tbody>
{this.state.rows}
</tbody>
...
)
...
}

Creating multiple page application using react

I have created SPA using react and hence my address bar does not change when I move to another view.Kindly help in changing the address bar. News feed Code is
import React from 'react';
import axios from 'axios';
import Stories from './Stories';
export default class NewsFeed extends React.Component {
constructor(props) {
super(props);
this.state = { feed: [], showPopUp: false, showStoryPopUp: false, readArr: [], importantArr: [], counterArr: [], deleteArr: [] };
this.handleClose = this.handleClose.bind(this);
this.handleCreateFeed = this.handleCreateFeed.bind(this);
this.handlePost = this.handlePost.bind(this);
this.handleCreateStories = this.handleCreateStories.bind(this);
}
componentWillMount() {
let self = this;
axios.get('src/rest/feed.json')
.then(function (response) {
let counterArr = self.state.counterArr;
let readArr = self.state.readArr;
let deleteArr = self.state.deleteArr;
for (let item of response.data) {
counterArr.push(0);
readArr.push(false);
deleteArr.push(false);
}
self.setState({ feed: response.data });
})
.catch(function (error) {
console.log(error);
});
}
changeImportant(index) {
let arr = this.state.importantArr;
arr[parseInt(index)] = !arr[parseInt(index)];
this.setState({ importantArr: arr });
}
changeReadFlag(index) {
let arr = this.state.readArr;
arr[parseInt(index)] = !arr[parseInt(index)];
this.setState({ readArr: arr });
}
decrement(index) {
let arr = this.state.counterArr;
arr[parseInt(index)] = arr[parseInt(index)] - 1;
this.setState({ counterArr: arr });
}
handleDelete(index) {
let arr = this.state.deleteArr;
arr[parseInt(index)] = !arr[parseInt(index)];
this.setState({ deleteArr: arr });
}
increment(index) {
let arr = this.state.counterArr;
arr[parseInt(index)] = arr[parseInt(index)] + 1;
this.setState({ counterArr: arr });
}
handlePost(header, description,broker) {
// console.log(document.querySelector("#title-input").value,document.querySelector("#description-input").value)
let tempObj = { imgsrc: "images1.jpg" };
// tempObj.header = document.querySelector("#title-input").value;
// tempObj.description = document.querySelector("#description-input").value;
tempObj.header = header;
tempObj.description = description;
tempObj.broker = broker;
let tempArr = this.state.feed;
tempArr.push(tempObj);
let counterArr = this.state.counterArr.push(0);
this.setState({ showStoryPopUp: false });
}
handleClose() {
this.setState({ showPopUp: false, showStoryPopUp: false })
}
handleCreateFeed() {
this.setState({ showPopUp: true });
}
handleCreateStories() {
this.setState({ showStoryPopUp: true });
}
render() {
return (
<div className="email-class lis-cls">
{this.state.showStoryPopUp ? <Stories handlePost={this.handlePost} handleClose={this.handleClose} /> : null}
{this.props.userType === 'admin' ? <button id="story-btn" type="button" className="btn btn-primary fixed-cls fa fa-pencil" style={{ display: this.state.showPopUp || this.state.showStoryPopUp ? 'none' : 'inline-block' }} onClick={this.handleCreateStories} > Create Stories</button> : null}
{this.state.feed.map((feed, index) => {
return (<div key={index} id={index} className={this.state.showPopUp || this.state.showStoryPopUp ? 'row row-feed hide-cls' : this.state.deleteArr[index] ? 'row row-feed hide-cls' : 'row row-feed'} >
<h4 className="list-header">{feed.header}</h4>
<img src={"src/img/" + feed.imgsrc} alt="Smiley face" height="100" width="100" />
<span className="feed-text">{feed.description}</span>
<div className="row">
<div className="col-md-1">
<span className={this.state.readArr[index] ? "fa fa-check-circle pull-right" : ""}></span>
</div>
<div className="col-md-1">
<div className="pull-right">
<div className="fa fa-arrow-up display-block-cls" onClick={this.increment.bind(this, index)}></div>
<div className={this.state.counterArr[index] == 0 ? "vote-cls" : this.state.counterArr[index] > 0 ? "vote-cls upvote" : "vote-cls downvote"}>
{this.state.counterArr[index]}
</div>
<div className="fa fa-arrow-down display-block-cls" onClick={this.decrement.bind(this, index)}> </div>
</div>
</div>
<div className="col-md-8">
<button id="read" className="btn btn-success fa fa-pencil" onClick={this.changeReadFlag.bind(this, index)}> Read </button>
<button id="delete" className="btn btn-danger fa fa-trash-o" onClick={this.handleDelete.bind(this, index)}> Delete </button>
<button className="btn btn-primary fa fa-exclamation" onClick={this.changeImportant.bind(this, index)}> Important </button>
</div>
</div>
<hr className={this.state.importantArr[index] ? 'imp-cls' : 'hr-cls'} />
</div>)
})}
</div>
);
}
}
stoies code is
import React from 'react';
export default class Stories extends React.Component {
constructor(props) {
super(props);
this.handleClose = this.handleClose.bind(this);
this.handlePost = this.handlePost.bind(this);
}
handleClose() {
this.props.handleClose();
}
handlePost(){
let header = document.querySelector("#title-input").value;
let description = document.querySelector("#description-story-input").value;
let broker = document.querySelector("#broker-input").value;
this.props.handlePost(header,description,broker);
}
render() {
return (<div className={'popup-cls add-story-top'}>
<div className={'add-story email-class'}>
<img onClick={this.handleClose} className="cross-cls" src="src/img/cross.png" alt="Smiley face" height="35" width="35" />
<h4 className="list-header">Create Stories</h4>
<table>
<tbody>
<tr>
<td>
Title
</td>
<td>
<input type="text" id="title-input" />
</td>
</tr>
<tr>
<td>
Description </td>
<td> <textarea rows="4" cols="50" id="description-story-input"></textarea>
</td>
</tr>
<tr>
<td>
Broker </td>
<td> <input type="text" id="broker-input" />
</td>
</tr>
<tr>
<td>
Ticker </td>
<td> <input type="text" id="ticker-input" />
</td>
</tr>
<tr>
<td>
Category </td>
<td> <input type="text" id="category-input" />
</td>
</tr>
<tr>
<td>
Direction </td>
<td> <input type="number" min="-2" max="2" defaultValue="0" id="direction-input" />
</td>
</tr>
<tr>
<td>
Rating </td>
<td> <input type="number" min="-5" max="5" defaultValue="0" id="rating-input" />
</td>
</tr>
<tr>
<td>
Score </td>
<td> <input type="number" min="-4" max="4" defaultValue="0" id="score-input" />
</td>
</tr>
<tr>
<td>
Trade Price </td>
<td> <input type="text" id="trade-price-input" />
</td>
</tr>
<tr>
<td>
Pre Trade Price </td>
<td> <input type="text" id="pre-trade-input" />
</td>
</tr>
<tr>
<td>
Attachment </td>
<td> <input type="file" id="attachment-input" />
</td>
</tr>
<tr>
<td>
Links </td>
<td> <input type="text" id="links-input" />
</td>
</tr>
<tr>
<td>
Percentage Change </td>
<td> <input type="text" id="percentage-change-input" />
</td>
</tr>
<tr>
<td>
Tags </td>
<td> <input type="text" id="tag-input" />
</td>
</tr>
<tr>
<td></td>
<td><button onClick={this.handlePost} id="post-btn" type="button" className="btn btn-primary fa fa-envelope" > Post</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>);
}
}
login code is
import React from 'react';
import axios from 'axios';
export default class Login extends React.Component {
constructor(props) {
super(props);
this.state = { login: false, userName: '', password: '' ,isValid : true};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChangeUserName = this.handleChangeUserName.bind(this);
this.handleChangePassword = this.handleChangePassword.bind(this);
}
handleSubmit() {
let self = this;
axios.get('src/rest/login.json')
.then(function (response) {
response.data.map((user)=>{
if(user.userName === self.state.userName && user.password === self.state.password ){
self.props.setLogin( true,user.role);
self.setState({isValid :true })
}else{
self.setState({isValid :false })
}
});
})
.catch(function (error) {
console.log(error);
});
}
handleChangeUserName(event){
this.setState({userName : event.target.value});
}
handleChangePassword(event){
this.setState({password : event.target.value});
}
render() {
return (
<div className="email-class email-class-div login-cls">
<div className="row row-login header-class">
<h3 className="float-class" >Login</h3>
<img src="src/img/star.png" className="float-class img-class" alt="Smiley face" height="35" width="35" />
</div>
<div className="error-div" style={{display : !this.state.isValid?'block':'none'}}>Invalid username or password</div>
<div className="row row-login androidTextbox">
<input className="col-md-6" type="text" placeholder="User ID/Email" onChange={this.handleChangeUserName}/>
</div>
<div className="row row-login androidTextbox">
<input className="col-md-6" type="password" placeholder="Password" onChange={this.handleChangePassword} />
</div>
<div className="row row-login submit-row" onClick={this.handleSubmit}>
<div id="button" >SUBMIT</div>
</div>
</div>
);
}
}
All three have same address.
I'm not sure how you are navigating without routes unless you are showing/hiding components. You could look into implementing the hash router

Resources