React App - Help me locate the cause of an infinite render loop - javascript

I'm somehow causing an infinite render loop when calling the appendModule function. I think it's being caused by the rows.js component as I recently split 4 rows out into seperate components and then imported them into one master rows.js and thats when the problem started. (note: I understand things aren't very tidy/optimal right now).
main.js
In this component I push module components into an array onclick
import React, { Component } from "react";
import Mod0 from "./modules/mod0";
import Mod1 from "./modules/mod1";
import Mod2 from "./modules/mod2";
class Main extends Component {
constructor() {
super();
this.state = {
moduleArray: this.moduleArray
};
this.moduleArray = [];
}
appendModule = x => e => {
switch (x) {
case 0:
this.moduleArray.push(
<div
key={this.moduleArray.length}
id={this.moduleArray.length}
style={{ fontSize: 0, lineHeight: 0 }}
>
<Mod0 />
</div>
);
break;
case 1:
this.moduleArray.push(
<div
key={this.moduleArray.length}
id={this.moduleArray.length}
style={{ fontSize: 0, lineHeight: 0 }}
>
<Mod1 />
</div>
);
break;
case 2:
this.moduleArray.push(
<div
key={this.moduleArray.length}
id={this.moduleArray.length}
style={{ fontSize: 0, lineHeight: 0 }}
>
<Mod2 />
</div>
);
break;
default:
}
this.setState({
moduleArray: this.moduleArray
});
};
console = () => {
return (
<div
id="console-root"
style={{ display: this.state.consoleState ? "block" : "none" }}
>
<div id="console">
<input
onClick={this.appendModule(0)}
value="Single col"
type="submit"
/>
<input
onClick={this.appendModule(1)}
value="Double col"
type="submit"
/>
<input
onClick={this.appendModule(2)}
value="Triple col"
type="submit"
/>
</div>
</div>
);
};
render() {
return (
<>
{this.console()}
<div id="email-root">
<div id="mods">{this.moduleArray}</div>
</div>
</>
);
}
}
export default Main;
mod0.js The below component is an example of the modules that contain the rows component.
import React from "react";
import Rows from "./../rows/rows";
class Mod1 extends React.Component {
render() {
return (
<table
id="Table1"
cellSpacing={0}
cellPadding={0}
border={0}
width="100%"
>
<tbody>
<tr>
<td
valign="top"
align="center"
style={{ borderCollapse: "collapse", borderWidth: 0 }}
bgcolor="#D9E1E2"
>
<table
className="Table2"
bgcolor="#FFFFFF"
cellSpacing={0}
cellPadding={0}
border={0}
width={680}
>
<tbody>
<tr>
<td
style={{ paddingTop: 24, paddingBottom: 24 }}
align="center"
>
<table
className="Table3"
align="center"
cellSpacing={0}
cellPadding={0}
border={0}
width={604}
>
<tbody>
<Rows />
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
);
}
}
export default Mod1;
rows.js The new <Rows /> component that I think is causing the problem. The rows are in an array like that so that I can add and remove them later. The problem persists if that are directly included.
import React from "react";
import Row0 from "./rows";
import Row1 from "./rows";
import Row2 from "./rows";
import Row3 from "./rows";
class Rows extends React.Component {
constructor() {
super();
this.state = {
rowArray: this.rowArray
};
this.rowArray = [
<Row0 key="0" />,
<Row1 key="1" />,
<Row2 key="2" />,
<Row3 key="3" />
];
console.log(this.rowArray);
}
render() {
return <>{this.rowArray}</>;
}
}
export default Rows;
row1.js An example of one of the individual row components imported into rows.js
import React from "react";
class Row1 extends React.Component {
render() {
return (
<tr>
<td
className="mobile-pad"
style={{
color: "#4a4a4a",
fontFamily: '"Campton", Helvetica, Arial, sans-serif',
fontSize: "26px",
lineHeight: "36px",
textAlign: "left",
paddingTop: 0,
paddingBottom: "18px"
}}
>
This is header copy
</td>
</tr>
);
}
}
export default Row1;

I believe your issue could be that instead of passing the function to onClick, you're calling the function. Try this instead:
onClick={() => { this.appendModule(0) }}
and let me know how it went
NOTE:
this uses arrow functions, introduced in ES6. You can also do
onClick={ this.appendModule.bind(this, 0) } if the former way doesn't work.

Related

Displaying data from SQL Table into React.js Frontend based on what option is selected in <select> tag

I am trying to display data so for example if "brkalk" is selected in the tag it only displays "brkalk" table row and its content. I tried doing ? and : along with useState but it did not work because I have the .map function so I had problems with closing "}". Some tags and table names are in Bosnian so don't worry about that :)
import React, { useState, useEffect } from 'react'
import axios from 'axios'
import "./Modal.css";
export default function Modal() {
const [modal, setModal] = useState(false);
const [database, setDatabase] = useState();
const getData = async () => {
try {
await axios.get("http://localhost:3001/data").then((response) => {
setDatabase(response.data)
})
}
catch (e) {
console.log(e)
}
}
useEffect(() => {
getData()
}, [])
const toggleModal = () => {
setModal(!modal);
};
if (modal) {
document.body.classList.add('active-modal')
} else {
document.body.classList.remove('active-modal')
}
return (
<>
<button onClick={toggleModal} className="btn-modal">
Tabela gotovinskih racuna
</button>
{modal && (
<div className="modal">
<div onClick={toggleModal} className="overlay"></div>
<div className="modal-content">
<h2>Tabela gotovinskih racuna</h2>
<select name="filters" id="filters">
<option value="brkalk">brkalk</option>
<option value="sifpar">sifpar</option>
</select>
<table style={{
border: "2px solid black",
margin: "auto",
marginTop: "25px",
marginBottom: "5px",
width: "95%"
}}>
<thead>
<tr>
<th>brkalk</th>
<th>datum</th>
<th>uempl</th>
<th>siforg</th>
<th>ziral</th>
<th>kartica</th>
<th>obust</th>
<th>sifpar</th>
<th>zakljucan</th>
<th>povrat</th>
<th>brkalkfisk</th>
<th>brkalkpov</th>
<th>regbroj</th>
<th>vozac</th>
<th>sifkart</th>
<th>idslog</th>
<th>idfirma</th>
<th>racunar</th>
<th>iznosf</th>
</tr>
</thead>
{database?.map((i) =>
<tbody style={{
textAlign: "center",
}}>
<tr>
<td>{i.brkalk}</td>
<td>{i.datum}</td>
<td>{i.uempl}</td>
<td>{i.siforg}</td>
<td>{i.ziral}</td>
<td>{i.kartica}</td>
<td>{i.obust}</td>
<td>{i.sifpar}</td>
<td>{i.zakljucan}</td>
<td>{i.povrat}</td>
<td>{i.brkalkfisk}</td>
<td>{i.brkalkpov}</td>
<td>{i.regbroj}</td>
<td>{i.vozac}</td>
<td>{i.sifkart}</td>
<td>{i.idslog}</td>
<td>{i.idfirma}</td>
<td>{i.racunar}</td>
<td>{i.iznosf}</td>
</tr>
</tbody>
)}
</table>
<button className="close-modal" onClick={toggleModal}>
CLOSE
</button>
</div>
</div>
)}
</>
);
}

TypeError: Cannot read property 'renderView' of null

I am trying to create a main page with 2 buttons in React.js: Option 1 and Option 2. If a user clicks on Option 1, this user should be redirected to Main1 page. If he/she clicks on Option 2, then this user is redirected to Main2. Sounds quite simple, but I get the following error:
TypeError: Cannot read property 'renderView' of null
App.render
C:/test/src/App.js:15
12 |
13 | render() {
14 |
> 15 | switch (this.state.renderView) {
| ^ 16 | case 1:
17 | return (
18 | <div className="App">
This is my code of App.js:
import React, { Component } from 'react';
import './App.css';
import { Header } from './components/Header';
import { Header1 } from './components/xai/Header1';
import { Header2 } from './components/fairness/Header2';
import { Main } from './components/Main';
import { Main1 } from './components/xai/Main1';
import { Main2 } from './components/fairness/Main2';
class App extends Component {
clickBtn = e => {
console.log(e.target.value);
this.setState({
renderView: +e.target.parentNode.value
});
};
render() {
switch (this.state.renderView) {
case 1:
return (
<div className="App">
<div className="App-header">
<Header1 />
</div>
<div className="App-main">
<Main1 />
</div>
</div>
);
case 2:
return (
<div className="App">
<div className="App-header">
<Header2 />
</div>
<div className="App-main">
<Main2 />
</div>
</div>
);
default:
return (
<div className="App">
<div className="App-header">
<Header />
</div>
<div className="App-main">
<Main clickBtn={this.clickBtn} />
</div>
</div>
);
}
}
}
export default App;
This is the code of Main.js where I have two buttons:
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import Grid from "#material-ui/core/Grid";
import CssBaseline from "#material-ui/core/CssBaseline";
import Card from "#material-ui/core/Card";
import CardContent from "#material-ui/core/CardContent";
import Typography from "#material-ui/core/Typography";
const useStyles = makeStyles({
card: {
minWidth: 350
},
button: {
fontSize: "12px",
margin: "theme.spacing.unit",
minWidth: 350
},
extendedIcon: {
marginRight: "theme.spacing.unit"
},
title: {
fontSize: '20px',
minWidth: 350,
margin: "theme.spacing.unit"
}
});
export function Main() {
const classes = useStyles();
return (
<React.Fragment>
<CssBaseline />
<Grid
container
spacing={0}
direction="column"
alignItems="center"
justify="center"
style={{ minHeight: "100vh" }}
>
<Card className={classes.card}>
<Typography align="center" className={classes.title}>
Select the option
</Typography>
<CardContent>
<Grid item xs={3}>
<Button
variant="contained"
size="medium"
color="primary"
className={classes.button}
value="1"
onClick={this.props.clickBtn}
>
Option 1
</Button>
</Grid>
<Grid item xs={3}>
<Button
variant="contained"
size="medium"
color="primary"
className={classes.button}
value="2"
onClick={this.props.clickBtn}
>
Option 2
</Button>
</Grid>
</CardContent>
</Card>
</Grid>
</React.Fragment>
);
}
How to fix this issue?
If other classes should be provided here, please let me know.
You didn't create state for the App component:
class App extends Component {
state = {renderView: null}
render(){
...
And note that this.clickBtn is undefined too, so you gotta define it.
since Main.js is a functional component it doesn't include the this keyword.
export function Main(props){
....
onClick={props.clickBtn}

Redirect to another page after GET call in modal form in React

I am trying to redirect to another page after fetch GET call from Modal from in react using history.props and I redirected but I don't know what should write there to show my actual data. I editied somehting i added something in history.props and componentDidMount in Report Here is my code:
import React, { Component } from "react";
import Datee from "./Date";
import { withRouter } from "react-router";
export class CarReports extends Component {
constructor(props) {
super(props);
this.state = {
selectOptions: [],
movie: [],
mov: "",
query: "",
results: []
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
async handleSubmit(e) {
let authToken = localStorage.getItem("Token");
try {
const from = e.target.elements.from.value;
const to = e.target.elements.to.value;
const selections = [...e.target.elements.selectOptions.options].filter(
opt => opt.selected
);
const selectedValues = selections.map(opt => opt.value);
const selectedString = selectedValues.join(",");
e.preventDefault();
const res = await fetch(
`http://localhost:8000/api/1/deliveries/report/?date__lte=${to}&date__gte=${from}&user=${selectedString}`,
{
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer " + JSON.parse(authToken)
}
}
);
const movie = await res.json();
console.log(movie);
this.setState({
movie
});
this.props.history.push({
pathname: "/carreport",
state: movie
});
} catch (e) {
console.log(e);
}
}
handleChange = e => {
let target = e.target;
let name = target.name;
//here
let value = Array.from(target.selectedOptions, option => option.value);
this.setState({
[name]: value
});
};
render() {
return (
<div id="car" class="modal">
<a
href="# "
rel="modal:close"
className="float-right text-white h4"
style={{
background: "#b71c1c",
borderRadius: "50%",
padding: "10px",
height: "32px",
lineHeight: "10px"
}}
>
×
</a>
<p className="mod" style={{ marginTop: "40px" }}>
CARS REPORT
</p>
<form style={{ marginTop: "20px" }} onSubmit={this.handleSubmit}>
<div>
<Datee />
</div>
<div className="form-group" style={{ marginTop: "20px" }}>
<label style={{ opacity: "0.6", fontSize: "10px" }}>
CHOOSE A CAR
</label>
<select
name="selectOptions"
style={{ width: "390px" }}
multiple={true}
onChange={this.handleChange}
value={this.state.selectOptions}
class="form-control"
>
<option value="1">Choose Car From List</option>
<option value="1">General Score</option>
<option value="2">Dynamic</option>
<option value="3">Speed</option>
<option value="4">Fuel Save</option>
</select>
</div>
<div style={{ marginTop: "50px" }}>
<center>
<button
type="submit"
value="Get Data"
className="btn btn-login text-white font-weight-bolder boxx "
style={{
height: "40px",
fontSize: "13px",
lineHeight: "30px",
width: "200px",
background: "rgba(183, 28, 28, 1)",
border: "none",
color: "white",
margin: "auto"
}}
>
RAPORT
</button>
</center>
</div>
</form>
</div>
);
}
}
export default withRouter(CarReports);
Here is my careport.js page where I want to display data. I don't know what to do here.
import React, { Component } from "react";
import ReactToExcel from "react-html-table-to-excel";
import CarReports from "../CarReports";
class Report extends Component {
componentDidMount() {
const movie = this.props.location.state.movie;
this.setState({
movie
});
console.log(movie)
}
render() {
return (
<div className="container" style={{ marginTop: "50px" }}>
<CarReports />
<div className="headerr" style={{ marginTop: "25px" }}>
<h6>CAR REPORT</h6>
<p className="p">From 12-17-2019 To 12-12-2019</p>
<div className="driver report">
<table className="table" id="table-to-xls">
<thead>
<tr>
<th>No</th>
<th>Full Name</th>
<th>Car Quantity</th>
<th>Mileage[Km]</th>
<th>Fuel Consumed[L]</th>
<th className="t">
Average Fuel<br></br> Consumed[L/100km]
</th>
<th className="t">Overspeed Distance[%]</th>
<th className="t">
Critical <br></br>Overspeed [qty.]
</th>
<th>Score: Overall</th>
</tr>
</thead>
<tfoot>
<tr className="thead">
<th></th>
<th>Summary</th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</tfoot>
</table>
<div style={{ marginTop: "40px" }}>
<center>
<a className="btn" href="#car" rel="modal:open" id="bttt">
NEW REPORT
</a>
<ReactToExcel
className="btn btn-success btn-lg bb"
table="table-to-xls"
filename="SoftbikeReport"
sheet="sheet 1"
buttonText="EXPORT CSV"
/>
</center>
</div>
</div>
</div>
</div>
);
}
}
export default Report;
You can pass state when redirecting using react-router's state API like so:
/** INSIDE CarReports.jsx */
export class CarReports extends Component {
...
async handleSubmit() {
...
const res = await fetch(...);
...
const movie = await res.json();
this.setState({ movie });
this.props.history.push({
pathname: "/carreport",
state: movie
});
}
...
}
You can then access the fetch results inside CarReport.jsx like so:
export class CarReport extends Component {
componentDidMount() {
const movie = this.props.location.state.movie;
}
...
}
here are some more resources:
How do i pass state through React_router?
https://tylermcginnis.com/react-router-pass-props-to-link/

How do I pass props from child to parent in React?

I have a dynamic table that renders a list of items that the user selects. The user can select an item from a drop down menu and then add that item to the list. As of right now I store the item list in the state of my component, which allows the table to render dynamically. What I would like is for the user to be able to click on an item in the table and be able to edit certain parts of that item, such as the quantity that they are selecting. Once the user clicks on that item from the table a Modal will appear that gets filled with the information from that specific item. My problem is that within the modal, when the user changes say the quantity of that item, I would like the Modal to close and then update the table with that value that the user changed.
Is there a way to pass this updated list of items back to the parent? Or is this not viable? and if so, what would be the right way to go about this. I will post my code below so that you guys can get a better understanding about what I'm trying to accomplish.
NOTE My modal isn't complete but I just would like to know how I can pass props back to the parent component.
Parent.js
export default Parent extends React.Component{
constructor(props){
super(props);
this.state = {
ItemList = [],
IDClicked = "",
}
AddItemHandler(){
...stuff to add to ItemList
}
RenderModal(){
let itemList = this.state.ItemList
<ItemModal items={itemList} />
}
RowClick(e){
//console.log(e.target.id)
var items = this.state.ItemList;
for(let i = 0; i < items.length; i++){
if(items[i].ID == e.target.id){
var Item = {
ID: items[i].ID,
PartName: items[i].PartName,
Quantity: items[i].Quantity
}
}
}
//console.log("Item clicked: " + JSON.stringify(Item));
this.setState({IDClicked: e.target.id})
(document.getElementById('myModal')).style.display = "block";
}
RenderTable(items){
var rows = [];
for(let i = 0; i < items.length; i++){
rows.push(
<tr style={{backgroundColor: '#B7BCDF'}} id={items[i].ID} key={i}>
<td style={{maxWidth: '20px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}} onClick={this.RowClick.bind(this)} id={items[i].ID}>
{items[i].PartName}
</td>
<td style={{maxWidth: '20px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}} onClick={this.RowClick.bind(this)} id={items[i].ID}>
{items[i].Description}
</td>
<td style={{maxWidth: '20px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}} onClick={this.RowClick.bind(this)} id={items[i].ID}>
{items[i].Type}
</td>
<td style={{maxWidth: '20px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}} onClick={this.RowClick.bind(this)} id={items[i].ID}>
{items[i].Quantity}
</td>
<td style={{maxWidth: '20px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}} onClick={this.RowClick.bind(this)} id={items[i].ID}>
{items[i].Units}
</td>
</tr>
)
}
return (
<div className="TableScroll2" style={{width: '99.5%', height: 'none'}}>
<table className="TableRows">
<tbody>
{rows}
</tbody>
</table>
</div>
);
}
render(){
return(
<div id="mymodal">
this.RenderModal();
</div>
<div style={{width: '50%', marginLeft: 'auto', marginRight: 'auto'}}>
<div className="ParTableContainer" style={{marginTop: '5%'}}>
<table className="PartTableHeaderContainer" style={{textAlign: 'center'}}>
<thead>
<tr>
<th style={{width: '20%'}}>Part Name</th>
<th style={{width: '20%'}}>Description</th>
<th style={{width: '20%'}}>Type</th>
<th style={{width: '20%'}}>QTY</th>
<th style={{width: '20%'}}>U/M</th>
</tr>
</thead>
</table>
</div>
{this.RenderTable(this.state.ItemList)}
</div>
<button style={{marginTop: '2%', marginBottom: '5%'}} onClick={this.AddItemHandler.bind(this)}>Add Item</button>
}
}
You cannot pass props from a child to the parent. There are however ways for a child to communicate with it's parent, which could be used to solve your problem.
The way to go is usually to use callbacks - pass a function from your parent to your child which the child can call to update the state of the parent. Here is an example that updates the parent state:
function Parent() {
const [counter, setCounter] = useState(0)
return (
<div>
Current: {state}
<Child increment={() => {
setCounter(current => current + 1)
}}}/>
</div>
)
}
function Child(props) {
return <button onClick={props.increment}>Click me</button>
}
(This example was done using hooks, which I strongly recommend learning)
Here it is without hooks:
class Parent extends Component {
constructor() {
this.state = { counter: 0 }
}
render() {
return (
<Child increment={() => {
this.setState((current) => {
return { counter: current.counter + 1 }
})
}}}/>
)
}
}
function Child(props) {
return <button onClick={props.increment}>Click me</button>
}

Drag and drop images in React

I'm trying to implement drag and drop behaviour using React JS and react-dropzone library with showing thumbnails.
The code is as follows:
import React from "react";
import ReactDOM from "react-dom";
import Dropzone from "react-dropzone";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
dropzone1: [],
dropzone2: []
};
}
addFilesToDropzone(files, dropzone) {
let files_with_preview = [];
files.map(file => {
file["preview"] = URL.createObjectURL(file);
files_with_preview.push(file);
});
const new_files = [...this.state[dropzone], ...files_with_preview];
this.setState({ [dropzone]: new_files });
}
render() {
return (
<div className="App">
<Dropzone
onDrop={files => {
this.addFilesToDropzone(files, "dropzone1");
}}
>
{({ getRootProps, getInputProps }) => (
<div {...getRootProps()} className="">
<input {...getInputProps()} />
<div style={{ height: 100, backgroundColor: "yellow" }}>
Drop some files here
{dropzone1.map(file => (
<img
src={file.preview}
alt={file.path}
style={{ width: 40, height: 40 }}
/>
))}
</div>
</div>
)}
</Dropzone>
<div style={{ display: "flex", flexDirection: "row", marginTop: 25 }}>
<div style={{ width: "100%" }}>
DROPZONE 2
<Dropzone
onDrop={files => {
this.addFilesToDropzone(files, "dropzone2");
}}
>
{({ getRootProps, getInputProps }) => (
<div {...getRootProps()} className="">
<input {...getInputProps()} />
<div style={{ height: 100, backgroundColor: "yellow" }}>
Drop some files here
{this.state.dropzone2.map(file => (
<img
src={file.preview}
alt="dsds"
style={{ width: 40, height: 40 }}
/>
))}
</div>
</div>
)}
</Dropzone>
</div>
</div>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Here is the example on codesandbox.
Everything works fine when I drag files from a folder on my computer, but I want to be able to drag thumbnails generated with dropzone 1 to a dropzone 2. But that doesn't work.
Any idea how to do that?
Yes, that doesn't work because that's not what react-dropzone is designed for. Quote from the website,
Simple React hook to create a HTML5-compliant drag'n'drop zone for files.
Use react-dnd or react-beautiful-dnd instead.
You can use another package: react-file-drop

Categories