how to edit and delete in api data table in reactjs - javascript

I am new to React. Currently, I am doing API fetch and displaying it in a table and adding edit delete in that table. I am successfully fetching API and I can add a new row along with API data, but I don't know how to edit and delete it. I have seen some questions but my code structure is different so I am unable to find the answer. Does any help please?
here is my code,
import React from 'react';
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
import { makeStyles,withStyles } from '#material-ui/core/styles';
import Fab from '#material-ui/core/Fab';
import AddIcon from '#material-ui/icons/Add';
import EditIcon from '#material-ui/icons/Edit';
import TextField from '#material-ui/core/TextField';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogContentText from '#material-ui/core/DialogContentText';
import DialogTitle from '#material-ui/core/DialogTitle';
import Button from '#material-ui/core/Button';
const useStyles = theme => ({
fab: {
margin: theme.spacing(1),
},
extendedIcon: {
marginRight: theme.spacing(1),
},
});
const Post = ({ body }) => {
  return (
    <table className=" table-striped">
      <thead>
       <tr>
      <th>Id</th>
      <th>Title</th>
      <th>Content</th>
      
      </tr>
      </thead>
      <tbody>
{body.map(post => {
const { _id, title, content } = post;
return (
 <tr key={_id}>
            <td> {_id!=''?_id: '-'}</td>
            
            <td> {title!='' ?title: '-'}</td>
            
            <td> {content!=''?content: '-'}</td>
<hr />
</tr>
);
})}
</tbody>
</table>
);
};
class App extends React.Component {
state = {
isLoading: true,
posts: [],
error: null,
open:false,
newData:[
{
_id:'',
title:'',
content:''
}
]
};
fetchPosts() {
const axios = require('axios');
axios
.get("https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/posts.json")
.then(response => {
this.setState({
posts: response.data.posts,
isLoading: false,
})
//)
} )
}
componentDidMount() {
this.fetchPosts();
}
handleClickOpen = () =>
this.setState({
open:true
})
;
handleClose = () =>
this.setState({
open:false
})
;
// handleClick = () =>
// {
// {this.handleClickOpen}
// {this.addItem}
// }
// ;
addItem = () =>
{
var temp = [];
var tempPosts = this.state.posts;
var newData = this.state.newData[0];
console.log(this.state.newData)
if(this.state.newData[0]._id && this.state.newData[0].title && this.state.newData[0].content){
tempPosts.push(newData);
console.log(tempPosts)
this.setState({posts: tempPosts})
}
}
render() {
const { isLoading, posts } = this.state;
return (
<React.Fragment>
<h1>React Fetch - Blog</h1>
<div>
<Button variant="outlined" color="primary" onClick=
{this.handleClickOpen}
>
Add
</Button>
<Dialog open={this.state.open} onClose={this.handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Subscribe</DialogTitle>
<DialogContent>
<TextField
autoFocus
margin="dense"
id="_id"
label="id"
value={this.state.newData._id}
onChange={(e)=> {
let{ newData } = this.state;
newData[0]._id=e.target.value;
this.setState({newData})
}}
type="text"
fullWidth
/>
<TextField
autoFocus
margin="dense"
id="title"
label="title"
value={this.state.newData.title}
onChange={(e)=> {
let{newData} =this.state;
newData[0].title=e.target.value;
this.setState({newData})
}}
type="text"
fullWidth
/>
<TextField
autoFocus
margin="dense"
id="content"
label="content"
value={this.state.newData.content}
onChange={(e)=> {
let{newData} =this.state;
newData[0].content=e.target.value;
this.setState({newData})
}}
type="text"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
this.addItem()
this.handleClose()
}}
color="primary">
Add
</Button>
<Button onClick={this.handleClose} color="primary">
cancel
</Button>
</DialogActions>
</Dialog>
</div>
<hr />
{!isLoading ? <Post body={posts} /> : <h3>Loading...</h3>}
</React.Fragment>
);
}
}
export default withStyles(useStyles)(App);

Related

is there a way to drag the events into the calender in react?

I am trying to create a dashboard with calender and task list.
(https://i.stack.imgur.com/0HoGJ.png)
this is the dashboard:
import React, { useState } from 'react';
import { Calendar, momentLocalizer } from "react-big-calendar";
import moment from "moment";
import withDragAndDrop from "react-big-calendar/lib/addons/dragAndDrop";
import "react-big-calendar/lib/addons/dragAndDrop/styles.css";
import "react-big-calendar/lib/css/react-big-calendar.css";
import AppBar from './AppBar';
import css from './App.css';
import events from './data';
import Box from '#mui/material/Box';
import Fab from '#mui/material/Fab';
import AddIcon from '#mui/icons-material/Add';
import Stack from '#mui/material/Stack';
import TextField from '#mui/material/TextField';
import Dialog from '#mui/material/Dialog';
import DialogActions from '#mui/material/DialogActions';
import DialogContent from '#mui/material/DialogContent';
import DialogContentText from '#mui/material/DialogContentText';
import DialogTitle from '#mui/material/DialogTitle';
import Button from '#mui/material/Button';
import DraggableEvent from './DraggableEvent';
const localizer = momentLocalizer(moment);
const DnDCalendar = withDragAndDrop(Calendar);
class Dashbord extends React.Component {
state = {
open: false,
events
};
state = {
events: [
{ id: 1, title: 'My Event 1', desc: 'My Event 1', duration: '30:00' },
{ id: 2, title: 'My Event 2', desc: 'My Event 10', duration: '35:00' },
{ id: 3, title: 'My Event 3', desc: 'My Event 100', duration: '60:00' },
{ id: 4, title: 'My Event 4', desc: 'My Event 1000', duration: '40:00' },
{ id: 5, title: 'My Event 5', desc: 'My Event 10000', duration: '50:00' }
],
removeAfterDrop: false
};
handleDrop = (info) => {
if (this.state.removeAfterDrop) {
// remove the element from the events list
this.setState((state) => {
state.events = state.events.filter((event) => event.id !== info.draggedEl.id);
return { events: state.events };
});
}
};
handleClickOpen() {
this.setState({open: true});
};
handleClose() {
this.setState({open: false});
};
constructor(props) {
super(props);
this.handleClickOpen = this.handleClickOpen.bind(this);
this.handleClose = this.handleClose.bind(this);
}
onEventResize = (data) => {
const { start, end } = data;
this.setState((state) => {
state.events[0].start = start;
state.events[0].end = end;
return { events: state.events };
});
};
onEventDrop = (event) => {
this.setState((state) => {
state.events.push(event);
return { events: state.events };
});
};
render() {
return (
<>
<div className="App">
<div className="cal">
<AppBar></AppBar>
</div>
<div id='main'>
<div id='calendar-container'>
<DnDCalendar id='calendar'
defaultDate={moment().toDate()}
defaultView="week"
events={this.state.events}
localizer={localizer}
onEventDrop={this.moveEvent}
onEventResize={this.onEventResize}
resizable
style={{ height: "100vh", width: "920px"
}}/>
</div>
<div id='external-events'>
<div id='Taskheader'>
<p>
<strong>Tasks</strong>
</p>
<Box sx={{ '& > :not(style)': { m: 1 } }}>
<Fab size="small" color="primary" aria-label="add" onClick={this.handleClickOpen}>
<AddIcon />
</Fab>
</Box>
</div>
<div>
{this.state.events.map((event) => (
<DraggableEvent key={event.id} event={event} />))}
</div>
<Dialog open={this.state.open} onClose={this.handleClose}>
<DialogTitle>Add New Task</DialogTitle>
<DialogContent>
<TextField
autoFocus
margin="dense"
label="Title"
type="text"
fullWidth
id="outlined-basic"
variant="outlined"
/>
<TextField
autoFocus
margin="dense"
label="Duration"
type="Number"
fullWidth
id="outlined-basic"
variant="outlined"
/>
<TextField
autoFocus
margin="dense"
label="Description"
type="text"
fullWidth
id="outlined-basic"
variant="outlined"
/>
</DialogContent>
<DialogActions>
<Button onClick={this.handleClose}>Cancel</Button>
<Button onClick={this.handleClose}>Submit</Button>
</DialogActions>
</Dialog>
</div>
<div>
</div>
</div>
</div>
</>
);
}
}
export default Dashbord;
this is the event component:
import React, { useState } from 'react';
import useStyles from './draggableEventStyles';
import Dialog from '#material-ui/core/Dialog';
import DialogTitle from '#material-ui/core/DialogTitle';
import DialogContent from '#material-ui/core/DialogContent';
import DialogContentText from '#material-ui/core/DialogContentText';
import DialogActions from '#mui/material/DialogActions';
import Button from '#mui/material/Button';
import TextField from '#mui/material/TextField';
const DraggableEvent = ({ event }) => {
const classes = useStyles();
const [open, setOpen] = useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<>
<div className={classes.event} onClick={handleClickOpen} key={event.id}>
<div className="row">
<div className="col-10">
<h3>{event.title}</h3>
<p>{event.desc}</p>
<div className="task-meta">
<img
src="https://icongr.am/feather/calendar.svg?size=12&color=b5b5b5"
alt="calendar" style={{ right: 30 }}
/>
{event.duration}
</div>
</div>
</div>
</div>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="event-dialog-title"
>
<DialogTitle id="event-dialog-title">{event.title}</DialogTitle>
<DialogContent>
<DialogContentText>
<TextField
autoFocus
margin="dense"
label="Title"
type="text"
fullWidth
id="outlined-basic"
variant="outlined"
/>
<TextField
autoFocus
margin="dense"
label="Duration"
type="Number"
fullWidth
id="outlined-basic"
variant="outlined"
/>
<TextField
autoFocus
margin="dense"
label="Description"
type="text"
fullWidth
id="outlined-basic"
variant="outlined"
/>
</DialogContentText>
</DialogContent>
</Dialog>
</>
);
};
export default DraggableEvent;
I'm attempting to create a weekly schedule with an area for tasks that the user may drag and drop them onto his schedule. Following the completion of the task components and dashboard creation, I was unable to figure out how to make the tasks draggable. I tried several ways to make the event component draggable, so the user can drop it inside the calender but i couldn't find a way. is there anyway i can do this ?
The Big Calendar documentation site has an example, in its 'Addons | Drag and Drop' area, for 'Drag and Drop (from outside the calendar)'. The 'Addons' area of the documentation provides the props of those addons that you will require.

Call function from material ui fonction

I want to pass a function as a props to my material ui function.
The given function is undefined in my material ui fonction.
import React, { Component } from 'react';
import styled from 'styled-components';
import InputBase from '#material-ui/core/InputBase';
import IconButton from '#material-ui/core/IconButton';
import Menu from '#material-ui/core/Menu';
import MenuItem from '#material-ui/core/MenuItem';
import SearchIcon from '#material-ui/icons/Search';
import Avatar from '#material-ui/core/Avatar';
import '../../css/App.css';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { withTranslation } from 'react-i18next';
import CreateNewGarden from './CreateNewGarden';
class Dashboard extends Component {
constructor(props) {
super(props);
this.state = {
};
this.myFunction = this.myFunction.bind(this);
}
myFunction() {
console.log("OK")
}
render() {
return (
<div>
<CreateNewGarden myFunction={this.myFunction}/>
</div>
);
}
}
const mapStateToProps = (state) => ({
});
Dashboard.propTypes = {
};
export default withTranslation()(withRouter(connect(mapStateToProps)(Dashboard)));
I send CreateNewGarden myFunction={this.myFunction} as a props and in my others file.
I have:
import React from 'react';
import { withStyles } from '#material-ui/core/styles';
import Dialog from '#material-ui/core/Dialog';
import MuiDialogTitle from '#material-ui/core/DialogTitle';
import MuiDialogContent from '#material-ui/core/DialogContent';
import MuiDialogActions from '#material-ui/core/DialogActions';
import IconButton from '#material-ui/core/IconButton';
import CloseIcon from '#material-ui/icons/Close';
import Typography from '#material-ui/core/Typography';
import Slider from '#material-ui/core/Slider';
import { useTranslation } from 'react-i18next';
import measureLogo from '../../assets/images/measure.png';
import { Button } from '../../components';
const styles = (theme) => ({
root: {
margin: 0,
padding: theme.spacing(2),
},
closeButton: {
position: 'absolute',
right: theme.spacing(1),
top: theme.spacing(1),
color: theme.palette.grey[500],
}
});
const DialogTitle = withStyles(styles)((props) => {
const {
children, classes, onClose, ...other
} = props;
return (
<MuiDialogTitle disableTypography className={classes.root} {...other}>
<Typography variant="h6">{children}</Typography>
{onClose ? (
<IconButton aria-label="close" className={classes.closeButton} onClick={onClose}>
<CloseIcon />
</IconButton>
) : null}
</MuiDialogTitle>
);
});
const DialogContent = withStyles((theme) => ({
root: {
padding: theme.spacing(2)
}
}))(MuiDialogContent);
const DialogActions = withStyles((theme) => ({
root: {
margin: 0,
padding: theme.spacing(1)
}
}))(MuiDialogActions);
export default function CustomizedDialogs(props) {
const [open, setOpen] = React.useState(false);
// eslint-disable-next-line no-unused-vars
const [height, setHeight] = React.useState(0);
// eslint-disable-next-line no-unused-vars
const [width, setWidth] = React.useState(0);
console.log("ici = " + props.myFunction)
const setSizeHeight = () => (e, value) => {
setHeight(value);
};
const setSizeWidth = () => (e, value) => {
setWidth(value);
};
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const { t } = useTranslation();
return (
<div className="marginCardComponent">
<Button
onClick={handleClickOpen}
text="dashboard.createGardenBtn"
type="submit"
/>
<Dialog onClose={handleClose} aria-labelledby="customized-dialog-title" open={open}>
<DialogTitle id="customized-dialog-title" className="centerText" onClose={handleClose}>
{t('dashboard.createGardenTitle')}
</DialogTitle>
<DialogContent className="logoMeasureParent">
<img src={measureLogo} alt="Logo" className="logoMeasure centerText" />
</DialogContent>
<DialogContent dividers>
<Typography className="centerText" gutterBottom>
{ t('dashboard.createGardenDetail') }
</Typography>
</DialogContent>
<div className="marginLeft3">
<p>{ t('dashboard.height') }</p>
</div>
<div className="centerSlider">
<Slider
/* eslint-disable-next-line react/destructuring-assignment */
defaultValue={0}
aria-labelledby="discrete-slider"
valueLabelDisplay="auto"
step={1}
marks
min={1}
max={20}
onChange={setSizeHeight()}
/>
</div>
<div className="marginLeft3">
<p>{ t('dashboard.width') }</p>
</div>
<div className="centerSlider">
<Slider
/* eslint-disable-next-line react/destructuring-assignment */
defaultValue={0}
aria-labelledby="discrete-slider"
valueLabelDisplay="auto"
step={1}
marks
min={1}
max={20}
onChange={setSizeWidth()}
/>
</div>
<DialogActions>
<Button
onClick={handleClose}
text="dashboard.cancelBtn"
type="submit"
/>
<Button
onClick={props.myFunction}
text="dashboard.createGardenBtn"
type="submit"
/>
</DialogActions>
</Dialog>
</div>
);
}
When i click on the button it does nothing and when i print myFunction it tell me undefined.
Why i can't give a props to the function and call myFunction ?
Thank you for your help.
You need to call it outside of onClick. Do it like this:
const handleClick = (e) => {
e.preventDefault()
props.myFunction()
}
And in the button:
<Button
onClick={handleClick}
text="dashboard.createGardenBtn"
type="submit"
/>
That will work. It just isnt letting you call it inside onClick
You can also just do this:
<Button
onClick={() => props.myFunction()}
text="dashboard.createGardenBtn"
type="submit"
/>

The React.js app sends multiple POST requests instead of a single request

I am developing React.js user interface that sends requests to the Django backend. The problem is that the React.js app sends multiple requests to the backend on a button click event and page reload event.
But I want to send just a single request when a button Predict is clicked in BottomControls.js. What is wrong in my code?
BatchFlights.js
import React, { Component, Fragment } from 'react';
import TopControls from "./layout/batch/TopControls"
import MainContent from "./layout/batch/MainContent"
import BottomControls from "./layout/batch/BottomControls"
import styles from "./layout/styles/styles";
import { withStyles } from "#material-ui/core/styles";
class BatchFlights extends Component {
constructor(props) {
super(props);
this.state = {
csvData: [],
holdingTime: 0,
prediction: 0,
labelWidth: 0
};
this.handleChange = this.handleChange.bind(this);
};
componentDidMount() {
this.fetchData();
};
updateDelay(prediction) {
this.setState(prevState => ({
prediction: prediction
}));
};
setCsvData = csvData => {
this.setState({
csvData
}, () => {
console.log(JSON.stringify(csvData))
});
}
fetchData = () => {
fetch("http://localhost:8000/batch_predict", {
method: "POST",
headers: {
'Accept': 'application/jsonp, text/plain, */*',
//'Content-Type': 'application/json'
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" // otherwise $_POST is empty
},
body: JSON.stringify({
holdingTime: this.state.holdingTime,
csvData: this.state.csvData
})
})
.then((resp) => {
return resp.json()
})
.then((data) => {
this.updateDelay(data.prediction)
})
.catch((error) => {
console.log(error, "catch the hoop")
})
};
handleChange = (name, event) => {
this.setState({
[name]: event.target.value
}, () => {
console.log("plannedDep",this.state.plannedDep)
});
};
handleReset = () => {
this.setState({
prediction: 0
});
};
render() {
return (
<Fragment>
<TopControls state={this.state} styles={this.props.classes} handleChange={this.handleChange} />
<MainContent state={this.state} styles={this.props.classes} setCsvData={this.setCsvData} />
<BottomControls state={this.state} styles={this.props.classes} fetchData={this.fetchData} handleReset={this.handleReset}/>
</Fragment>
);
}
}
const StyledBatchFlights = withStyles(styles)(BatchFlights);
export default StyledBatchFlights;
CSVDataTable.js
import React, { Component } from 'react';
import { CsvToHtmlTable } from 'react-csv-to-table';
import ReactFileReader from 'react-file-reader';
import Button from '#material-ui/core/Button';
const sampleData = `
NUM,AIRLINE_ARR_ICAO,WAKE,SIBT,SOBT,PLANNED_TURNAROUND,DISTANCE_FROM_ORIGIN,DISTANCE_TO_TARGET
1,VLG,M,2016-01-01 04:05:00,2016-01-01 14:10:00,45,2000,2000
2,VLG,M,2016-01-01 04:05:00,2016-01-01 14:10:00,45,2000,2000
`;
class CSVDataTable extends Component {
state={
csvData: sampleData
};
handleFiles = files => {
var reader = new FileReader();
reader.onload = (e) => {
// Use reader.result
this.setState({
csvData: reader.result
})
this.props.setCsvData(reader.result)
}
reader.readAsText(files[0]);
}
render() {
return <div>
<ReactFileReader
multipleFiles={false}
fileTypes={[".csv"]}
handleFiles={this.handleFiles}>
<Button
variant="contained"
color="primary"
>
Load data
</Button>
</ReactFileReader>
<CsvToHtmlTable
data={this.state.csvData || sampleData}
csvDelimiter=","
tableClassName="table table-striped table-hover"
/>
</div>
}
}
export default CSVDataTable;
BottomControls.js
import React, { Component, Fragment } from 'react';
import CssBaseline from '#material-ui/core/CssBaseline';
import Grid from '#material-ui/core/Grid';
import Card from '#material-ui/core/Card';
import CardActionArea from '#material-ui/core/CardActionArea';
import CardContent from '#material-ui/core/CardContent';
import AppBar from '#material-ui/core/AppBar';
import Button from '#material-ui/core/Button';
import Icon from '#material-ui/core/Icon';
class BottomControls extends Component {
render() {
return (
<Fragment>
<CssBaseline />
<AppBar position="fixed" color="primary" className={this.props.styles.appBar}>
<div className={this.props.styles.toolbar}>
<Grid container spacing={24}>
<Grid item xs={6} sm={3}>
<Button variant="contained" color="primary" onClick={this.props.fetchData} className={this.props.styles.button}>
Predict
<Icon className={this.props.styles.rightIcon}>send</Icon>
</Button>
<Button variant="contained" color="primary" onClick={this.props.handleReset} className={this.props.styles.button}>
Reset
<Icon className={this.props.styles.rightIcon}>clear</Icon>
</Button>
</Grid>
<Grid item xs={6} sm={2}>
<Card className={this.props.styles.predictedDelay}>
<CardActionArea>
<CardContent>
<div className={this.props.styles.predictedDelayText}>
Prediction: {this.props.state.prediction} <span> </span>
</div>
</CardContent>
</CardActionArea>
</Card>
</Grid>
</Grid>
</div>
</AppBar>
</Fragment>
);
}
}
export default BottomControls;
Maybe is because your binding an arrow function, this.handleChange, but I don't see any problem besides that
I deleted this:
componentDidMount() {
this.fetchData();
};
Now the request is sent only on a button click. It is not sent on a page reload.

How to pass props from a reusable form in React

I'm building a reusable form in React to be used as login and signin forms in may app.
I'm using Axios to send the data to the database.
But I can't send the input values as props to my UserSignIn component to use them in a function with Axios. Here's the code :
How do I get these props ? Thanks
AccountForm component :
import React, { Component } from "react";
import {Grid,Input, InputLabel, FormControl, Typography, Button, Paper,
} from "#material-ui/core";
class AccountForm extends Component {
constructor(props) {
super(props);
this.state = {
userSignUpName: "",
userSignUpEmail: ""
}
handleChange(e) {
this.setState({
[e.target.id]: e.target.value
});
}
render() {
const {
classes,
formType,
onCreateAccount,
onSubmit,
onBlurCheck,
message,
title
} = this.props;
const {
userSignUpName,
userSignUpEmail,
} = this.state;
return (
<div>
<Grid container justify="center">
<Grid item xs={10}>
<Paper>
<form
onChange={e => this.handleChange(e)}
onSubmit={onSubmit}
>
<Typography variant="subheading" color="primary"
align="center">
{title}
</Typography>
{formType === "signin" && (
<FormControl>
<InputLabel htmlFor="name">Nom</InputLabel>
<Input id="userSignUpName" name="name" type="text" />
</FormControl>
)}
<FormControl>
<InputLabel htmlFor="email">Email</InputLabel>
<Input id="userSignUpEmail" type="email" name="email" />
</FormControl>
</form>
</Paper>
</Grid>
</Grid>
</div>
);
}
}
export default AccountForm;
UserSignIn component :
import React, { Component } from "react";
import axios from "axios";
import config from "../../assets/lib/axiosConfig";
import { AccountForm } from "./Index";
class UserSignIn extends Component {
constructor(props) {
super(props);
this.state = {
formType: "signin",
title: "Create account"
};
}
handleSubmit(e) {
e.preventDefault();
axios(
config(
{
name: this.props.userSignUpName,
email: this.props.userSignUpEmail,
},
"/createAccount",
"post"
)
).then(res => {
const { success, error, token } = res.data;
if (success) {
localStorage.setItem("AuthToken", token);
this.props.redirectTo();
}
if (error) {
this.setState({ message: error });
}
});
}
render() {
const { prevPath, history, userSignUpName } = this.props;
// I can't get userSignUpName props
const { formType, message, title } = this.state;
return (
<AccountForm
{...this.props}
title={title}
onSubmit={e => this.handleSubmit(e)}
formType={formType}
redirectTo={
prevPath !== null && prevPath === "/mycart"
? () => history.push("/payment")
: () => history.push("/")
}
/>
);
}
}
export default UserSignIn;
Thanks
You can pass form data as handleSubmit parameters
AccountForm component :
<form
onChange={e => this.handleChange(e)}
onSubmit={(e) => {
e.preventDefault()
onSubmit({
userSignUpName: this.state.userSignUpName,
userSignUpEmail: this.state.userSignUpEmail,
})
}}
>
UserSignIn component :
handleSubmit(params) {
axios(
config(
{
name: params.userSignUpName,
email: params.userSignUpEmail,
},
//....
}

How can I avoid to get too much recursion when storing data using firebase?

I'm creating a small application using only React.js, material-ui and firebase. I don't want to use Redux now in order to be familiar more with react.
I create a form which is described by:
User.jsx:
import React, { Component } from 'react'
import Button from '#material-ui/core/Button'
import Grid from '#material-ui/core/Grid';
import { withStyles } from '#material-ui/core/styles';
import PropTypes from 'prop-types';
import Typography from '#material-ui/core/Typography';
import moment from 'moment';
import db from '../db/config';
import InputTextField from './textField';
import RadioGroup from './radioGroup';
import SnackBar from './snackBar';
const styles = (theme) => ({
button: {
margin: theme.spacing.unit,
},
root: {
display: 'flex',
marginTop: theme.spacing.unit * 8,
padding: theme.spacing.unit * 3,
},
item: {
padding: theme.spacing.unit * 2
}
});
class User extends Component {
state = {
birthday: moment().format('YYYY-MM-DD'),
message: '',
name: '',
open: false,
gender: 'male',
};
handleChange = name => event => {
this.setState({
[name]: event.target.value,
});
};
handleClose = (event, reason) => {
if (reason === 'clickaway') {
return;
}
this.setState({ open: false });
};
handleSubmit = (event) => {
event.preventDefault();
const {
birthday,
name,
gender,
} = this.state;
console.log(`birthday: ${birthday} \n` +
`Name: ${name} \n` +
`gender: ${gender} \n`)
const ref = db.ref('users/');
ref.orderByChild('name').on('child_added', (snapshot) => {
const existedName = (snapshot.val().name).toLowerCase().trim();
const newName = name.toLowerCase().trim();
if(existedName === newName){
this.setState({
open: true,
message: 'Name already exists!!',
})
} else {
ref.push({
name: name.trim(),
gender,
birthday
})
.then(() => {
this.setState({
open: true,
message: 'saved successfully!!',
})
return true;
})
.catch((error) => {
this.setState({
open: true,
message: `Error adding baby: ${error}`,
})
return false;
});
}
})
}
render(){
const { classes } = this.props;
const {
birthday,
message,
name,
open,
gender,
} = this.state;
return (
<div className={classes.root}>
<Grid
container
spacing={40}
justify='center'
direction="column"
alignItems="center"
>
{open && (
<SnackBar
handleClose={this.handleClose}
message={message}
open
/>
)}
<Typography
align="center"
gutterBottom
variant="title"
>
Add New User
</Typography>
<form onSubmit={this.handleSubmit} className={classes.form}>
<Grid item className={classes.item} xs={12}>
<InputTextField
label="Name"
handleChange={this.handleChange('name')}
required
value={name}
type="text"
/>
</Grid>
<Grid item className={classes.item}>
<InputTextField
label="Birthday"
handleChange={this.handleChange('birthday')}
required
value={birthday}
type="date"
InputLabelProps={{shrink: true}}
/>
</Grid>
<Grid item className={classes.item}>
<RadioGroup
name="Gender"
handleChange={this.handleChange('gender')}
value={gender}
/>
</Grid>
<Grid item className={classes.item}>
<Grid
container
direction="row"
>
<Grid item>
<Button
variant="contained"
color="primary"
className={classes.button}
>
Cancel
</Button>
</Grid>
<Grid item>
<Button
variant="contained"
color="primary"
className={classes.button}
type='submit'
>
Save
</Button>
</Grid>
</Grid>
</Grid>
</form>
</Grid>
</div>
)
}
}
User.propTypes = {
classes: PropTypes.object.isRequired,
}
export default withStyles(styles)(User);
SnackBar.jsx:
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import Snackbar from '#material-ui/core/Snackbar';
import IconButton from '#material-ui/core/IconButton';
import CloseIcon from '#material-ui/icons/Close';
const styles = theme => ({
close: {
width: theme.spacing.unit * 4,
height: theme.spacing.unit * 4,
},
});
const SimpleSnackbar = (props) => {
const {
classes,
handleClose,
message,
open,
} = props;
return (
<div>
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
open={open}
autoHideDuration={6000}
onClose={handleClose}
ContentProps={{
'aria-describedby': 'message-id',
}}
message={<span id="message-id">{message}</span>}
action={[
<Button key="undo" color="secondary" size="small" onClick={handleClose}>
UNDO
</Button>,
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={handleClose}
>
<CloseIcon />
</IconButton>,
]}
/>
</div>
);
}
SimpleSnackbar.propTypes = {
classes: PropTypes.object.isRequired,
handleClose: PropTypes.func.isRequired,
message: PropTypes.string.isRequired,
open: PropTypes.bool.isRequired,
};
export default withStyles(styles)(SimpleSnackbar);
When I enter the different attributes of the form, I got this warning in the console: Firebase Warning
Added to the message too much recursion described by Firebase: too much recursion
I'm beginner to firebase and it's my first app using it.I think that I missed something or I didn't use the suitable function in order to fetch if a given name already exists. Unless it will be saved. I will be grateful if anyone tried to help me to fix the warning and the error displayed on the console.
I tried this solution and it works:
handleSubmit = (event) => {
event.preventDefault();
const {
birthday,
name,
gender,
} = this.state;
const ref = db.ref('users/');
const onChildAdded = (snapshot) => {
if(snapshot.exists()){
this.setState({
open: true,
message: 'Name already exists!!',
})
} else {
ref.push({
name: name.trim(),
gender,
birthday
})
.then(() => {
this.setState({
open: true,
message: 'saved successfully!!',
})
return true;
})
.catch((error) => {
this.setState({
open: true,
message: `Error adding baby: ${error}`,
})
return false;
});
}
}
ref.orderByChild('name').equalTo(name).once('value').then(onChildAdded);
}

Categories

Resources