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,
},
//....
}
Related
I am using react hooks mostly in my current app. I need the below code expressed as react hooks, without the this.state and this.props. My current app is expressed entirely as React Hooks. If you see it closely, the below code is writing out SQL query code with the click of a button. I need that functionality in my current app, but I don't know how to assimilate that in my app. Any ideas?
import React from 'react';
import { Row, Col, Tabs, Spin, Card, Alert, Tooltip, Icon, Button } from 'antd';
import cubejs from '#cubejs-client/core';
import { QueryRenderer } from '#cubejs-client/react';
import sqlFormatter from "sql-formatter";
import JSONPretty from 'react-json-pretty';
import Prism from "prismjs";
import "./css/prism.css";
const HACKER_NEWS_DATASET_API_KEY = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpIjozODU5NH0.5wEbQo-VG2DEjR2nBpRpoJeIcE_oJqnrm78yUo9lasw'
class PrismCode extends React.Component {
componentDidMount() {
Prism.highlightAll();
}
componentDidUpdate() {
Prism.highlightAll();
}
render() {
return (
<pre>
<code className='language-javascript'>
{ this.props.code }
</code>
</pre>
)
}
}
const tabList = [{
key: 'code',
tab: 'Code'
}, {
key: 'sqlQuery',
tab: 'Generated SQL'
}, {
key: 'response',
tab: 'Response'
}];
class CodeExample extends React.Component {
constructor(props) {
super(props);
this.state = { activeTabKey: 'code' };
}
onTabChange(key) {
this.setState({ activeTabKey: key });
}
render() {
const { codeExample, resultSet, sqlQuery } = this.props;
const contentList = {
code: <PrismCode code={codeExample} />,
response: <PrismCode code={JSON.stringify(resultSet, null, 2)} />,
sqlQuery: <PrismCode code={sqlQuery && sqlFormatter.format(sqlQuery.sql())} />
};
return (<Card
type="inner"
tabList={tabList}
activeTabKey={this.state.activeTabKey}
onTabChange={(key) => { this.onTabChange(key, 'key'); }}
>
{ contentList[this.state.activeTabKey] }
</Card>);
}
}
const Loader = () => (
<div style={{textAlign: 'center', marginTop: "50px" }}>
<Spin size="large" />
</div>
)
const TabPane = Tabs.TabPane;
class Example extends React.Component {
constructor(props) {
super(props);
this.state = { showCode: false };
}
render() {
const { query, codeExample, render, title } = this.props;
return (
<QueryRenderer
query={query}
cubejsApi={cubejs(HACKER_NEWS_DATASET_API_KEY)}
loadSql
render={ ({ resultSet, sqlQuery, error, loadingState }) => {
if (error) {
return <Alert
message="Error occured while loading your query"
description={error.message}
type="error"
/>
}
if (resultSet && !loadingState.isLoading) {
return (<Card
title={title || "Example"}
extra={<Button
onClick={() => this.setState({ showCode: !this.state.showCode })}
icon="code"
size="small"
type={this.state.showCode ? 'primary' : 'default'}
>{this.state.showCode ? 'Hide Code' : 'Show Code'}</Button>}
>
{render({ resultSet, error })}
{this.state.showCode && <CodeExample resultSet={resultSet} codeExample={codeExample} sqlQuery={sqlQuery}/>}
</Card>);
}
return <Loader />
}}
/>
);
}
};
export default Example;
It's quite easy to convert a class to a functional component.
Remember these steps:
class => const
// Class
export class Example
// FC
export const Example
componentLifeCycles => useEffect
// Class lifecycle
componentDidMount() {
// logic here
}
// FC
useEffect(() => {
// logic here
})
render => return
// Class
render () {
return (<Component/>)
}
// FC
return (<Component />)`
constructor => useState
// Class
constructor(props) {
this.state.val = props.val
}
// FC
const [val, setVal] = useState(props.val)
setState => second arg from useState
// Class
constructor() {
this.state.val = val // constructor
}
this.setState({ val }) // class
// FC
const[val, setVal] = useState(null)
setVal("someVal")
TLDR: Solution
import React, { useEffect, useState } from "react"
import { Tabs, Spin, Card, Alert, Button } from "antd"
import cubejs from "#cubejs-client/core"
import { QueryRenderer } from "#cubejs-client/react"
import sqlFormatter from "sql-formatter"
import Prism from "prismjs"
import "./css/prism.css"
const HACKER_NEWS_DATASET_API_KEY =
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpIjozODU5NH0.5wEbQo-VG2DEjR2nBpRpoJeIcE_oJqnrm78yUo9lasw"
const PrismCode: React.FC = ({ code }) => {
useEffect(() => {
Prism.highlightAll()
})
return (
<pre>
<code className="language-javascript">{code}</code>
</pre>
)
}
const TabList = [
{
key: "code",
tab: "Code",
},
{
key: "sqlQuery",
tab: "Generated SQL",
},
{
key: "response",
tab: "Response",
},
]
const CodeExample: React.FC = ( { codeExample, resultSet, sqlQuery } ) => {
const [activeTabKey, setActiveTab] = useState("code")
const onTabChange = (key) => setActiveTab(key)
const contentList = {
code: <PrismCode code={codeExample} />,
response: <PrismCode code={JSON.stringify(resultSet, null, 2)} />,
sqlQuery: (
<PrismCode code={sqlQuery && sqlFormatter.format(sqlQuery.sql())} />
),
}
return (
<Card
type="inner"
tabList={TabList}
activeTabKey={activeTabKey}
onTabChange={(key) => {
onTabChange(key)
}}
>
{contentList[activeTabKey]}
</Card>
)
}
const Loader = () => (
<div style={{ textAlign: "center", marginTop: "50px" }}>
<Spin size="large" />
</div>
)
const TabPane = Tabs.TabPane
const Example: React.FC = ({ query, codeExample, render, title }) => {
const [showCode, toggleCode] = useState(false)
return (
<QueryRenderer
query={query}
cubejsApi={cubejs(HACKER_NEWS_DATASET_API_KEY)}
loadSql
render={({ resultSet, sqlQuery, error, loadingState }) => {
if (error) {
return (
<Alert
message="Error occured while loading your query"
description={error.message}
type="error"
/>
)
}
if (resultSet && !loadingState.isLoading) {
return (
<Card
title={title || "Example"}
extra={
<Button
onClick={() =>
toggleCode(!this.state.showCode)
}
icon="code"
size="small"
type={showCode ? "primary" : "default"}
>
{showCode ? "Hide Code" : "Show Code"}
</Button>
}
>
{render({ resultSet, error })}
{showCode && (
<CodeExample
resultSet={resultSet}
codeExample={codeExample}
sqlQuery={sqlQuery}
/>
)}
</Card>
)
}
return <Loader />
}}
/>
)
}
export default Example
I am trying to have a button enabled in a modal when text is entered in an input field. But my form is built in another class and is used in a parent class. How can I pass an onChange method my form component.
Here is my parent component:
import React from 'react';
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle
} from '#material-ui/core';
import CompanyFinancialModalForm from '../CompanyFinancialModalForm/CompanyFinancialModalForm';
interface CompanyFinancialModalState {
addEnabled: boolean;
}
interface CompanyFinancialModalProps {
open: boolean;
onClose: () => void;
}
export class CompanyFinancialModal extends React.Component<
CompanyFinancialModalProps,
CompanyFinancialModalState
> {
constructor(props: CompanyFinancialModalProps) {
super(props);
this.state = {
addEnabled: false
};
}
private enableButton = () => {
this.setState({ addEnabled: true});
}
public render() {
const { open, onClose } = this.props;
const { addEnabled } = this.state;
return (
<>
<Dialog
open={open}
onClose={onClose}
className="company-financial-modal"
>
<DialogTitle id="company-financial-modal-title">
{'Company and Financial Data'}
</DialogTitle>
<DialogContent>
<CompanyFinancialModalForm onChange={this.enableButton}/>
</DialogContent>
<DialogActions>
<Button
id="company-financial-modal-add"
disabled={!addEnabled}
onClick={onClose}
color="primary"
>
Add
</Button>
<Button
id="company-financial-modal-cancel"
onClick={onClose}
color="secondary"
autoFocus={true}
>
Cancel
</Button>
</DialogActions>
</Dialog>
</>
);
}
}
export default CompanyFinancialModal;
Here is my class that my form is in:
import React from 'react';
import axios from 'axios';
import { Form, Field } from 'react-final-form';
import { TextField, Select } from 'final-form-material-ui';
import {
Paper,
Grid,
MenuItem,
} from '#material-ui/core';
export interface IValues {
company_name: string;
critical_technology: [];
}
export interface IFormState {
[key: string]: any;
values: IValues[];
submitSuccess: boolean;
}
export default class CompanyFinancialModalForm extends React.Component<{}, IFormState> {
constructor(props: {}) {
super(props);
this.state = {
company_name: '',
critical_technology: [],
values: [],
submitSuccess: false
};
}
private processFormSubmission = (e: React.FormEvent<HTMLFormElement>): void => {
e.preventDefault();
this.setState({ loading: true });
const formData = {
company_name: this.state.company_name,
critical_technology: this.state.critical_technology
};
this.setState({
submitSuccess: true,
values: [...this.state.values, formData],
loading: false
});
axios.post(`http://localhost:8081/companies`, formData);
}
private onChange = (e: React.FormEvent<HTMLInputElement>) => {
const { name, value } = e.target;
// other form-related logic
this.props.onChange({ name, value }, e);
}
public render() {
const { submitSuccess, loading } = this.state;
const { onChange } = this.props;
return (
<div>
<Form
onSubmit={this.processFormSubmission}
// validate={this.validateForm}
render={({ handleSubmit,/* reset, submitting, pristine, values*/ }) => (
<form onSubmit={handleSubmit} noValidate>
<Paper style={{ padding: 16 }}>
<Grid container alignItems="flex-start" spacing={2}>
<Grid item xs={6}>
<Field
fullWidth
required
name="companyName"
component={TextField}
type="text"
label="Company Name"
onChange={onChange}
/>
</Grid>
<Grid item xs={12}>
<Field
name="critical_technology"
label="Critical Technology"
component={Select as any}
>
<MenuItem value="hypersonics">Hypersonics</MenuItem>
<MenuItem value="directed_energy">Directed Energy</MenuItem>
<MenuItem value="command_control_and_communications">Command, Control and Communications </MenuItem>
<MenuItem value="space_offense_and_defense">Space Offense and Defense</MenuItem>
<MenuItem value="cybersecurity">Cybersecurity</MenuItem>
<MenuItem value="artificial_intelligence_machine_learning">Artificial Intelligence/Machine Learning</MenuItem>
<MenuItem value="missile_defense">Missile Defense</MenuItem>
<MenuItem value="quantum_science_and_computing">Quantum Science and Computing </MenuItem>
<MenuItem value="microelectronics">Microelectronics</MenuItem>
<MenuItem value="autonomy">Autonomy</MenuItem>
</Field>
</Grid>
</Grid>
</Paper>
</form>
)}
/>
</div>
);
}
}
I want to pass a prop to <CompanyFinancialModalForm /> that enables the add button when the Textfield has text typed into it.
For future reference, it will be more beneficial if you only include the relevant code, because it takes more time to find when scrolling through irrelevant code, anyways:
I'm not 100% clear on what you're looking for, but I'll try to answer what I think I understand. You can add an onChange method on your parent component, and pass that as a prop to the form, and the form can call that function every time it runs it's own onChange method. Below is a simplified version:
class Parent extends Component {
state = {
buttonEnabled: false,
// formInputValue: '', <-- if you need this
};
// - omitting constructor/bind for simplicity for now
onChange({ name, value }, e) {
// your logic to determine whether button is enabled or not
// this is just me guessing what you want to implement
if (value) this.setState({ buttonEnabled: true });
else this.setState({ buttonEnabled: false });
}
render() {
return (
<Fragment>
<YourForm onChange={this.onChange} />
<Button enabled={this.state.buttonEnabled} />
</Fragment>
);
}
}
class YourForm extends Component {
onChange(e) {
const { name, value } = e.target;
// other form-related logic
this.props.onChange({ name, value }, e);
}
}
is this what you're looking for?
You can simply pass a child a reference to a function that exists in the parent and then use the parent's function to validate and enable the button.
Codesandbox Demo
Simplified Code:
function Child (props) {
return (
<input type="text" onChange={props.doIt}/>
)
}
function App() {
const [disabled, setDisabled] = useState(true);
function doIt(e) {
setDisabled(e.currentTarget.value.length === 0);
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<Child doIt={doIt} />
<button disabled={disabled}>Add</button>
</div>
);
}
I have tried too many times for this below form (React-with-redux), my textbox is not taking any inputs, please suggest me,
Thanks for help, Buddy
import React from 'react';
import './login.css';
import {connect} from 'react-redux'
import { change, Field, reduxForm } from 'redux-form';
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
import { login } from './Actions';
export const renderTextField = ({
input,
label,
id,
multiLine,
rowsMax,
fullWidth,
disabled,
hintText,
defaultValue,
onChange,
maxLength,
loader,
meta: { touched, error },
customError,
autoFocus,
floatingLabelFixed,
...custom
}) => {
return (
<TextField
id={id}
defaultValue={defaultValue}
autoFocus={autoFocus}
floatingLabelText={label}
floatingLabelFixed={floatingLabelFixed}
errorText={touched && (error || customError)}
multiLine={multiLine}
hintText={hintText}
rowsMax={rowsMax}
disabled={disabled}
fullWidth={true}
className="valuefont"
autoComplete='new-type'
onChange={(event) => onChange}
maxLength={maxLength}
floatingLabelStyle={{ top: '30px', color: '#7a7a7a' }}
floatingLabelFocusStyle={{ color: '#01B9C1' }}
style={{ height: '62px ' }}
inputStyle={{ marginTop: '10px' }}
{...input}
{...custom}
/>
);
};
class Login extends React.Component{
constructor(props){
super(props);
}
handleChange = (e)=>{
// setValues({ ...values, [e.target.name]: e.target.value });
}
onSubmit = (formProps)=>{
debugger
}
render(){
const { handleSubmit } = this.props;
return(
<form noValidate onSubmit={handleSubmit(this.onSubmit)}>
<div>
<Field
name="firstName"
component={renderTextField}
label="First Name"
/>
</div>
<Button variant="contained" color="primary" type="submit">
Login
</Button>
</form>
)
}
}
const mapStateToProps = (state)=>{
return {
loading : state.loginReducer.loading,
error : state.loginReducer.error
}
}
export default connect(mapStateToProps,{login})(reduxForm({
form: 'LoginForm',
keepDirtyOnReinitialize: true,
enableReinitialize: true,
})(Login))
Use renderTextField instead of Field
import React from 'react';
import './login.css';
import {connect} from 'react-redux'
import { change, Field, reduxForm } from 'redux-form';
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
import { login } from './Actions';
export const renderTextField = ({
input,
label,
id,
multiLine,
rowsMax,
fullWidth,
disabled,
hintText,
defaultValue,
onChange,
maxLength,
loader,
meta: { touched, error },
customError,
autoFocus,
floatingLabelFixed,
...custom
}) => {
return (
<TextField
id={id}
defaultValue={defaultValue}
autoFocus={autoFocus}
floatingLabelText={label}
floatingLabelFixed={floatingLabelFixed}
errorText={touched && (error || customError)}
multiLine={multiLine}
hintText={hintText}
rowsMax={rowsMax}
disabled={disabled}
fullWidth={true}
className="valuefont"
autoComplete='new-type'
onChange={(event) => onChange}
maxLength={maxLength}
floatingLabelStyle={{ top: '30px', color: '#7a7a7a' }}
floatingLabelFocusStyle={{ color: '#01B9C1' }}
style={{ height: '62px ' }}
inputStyle={{ marginTop: '10px' }}
{...input}
{...custom}
/>
);
};
class Login extends React.Component{
constructor(props){
super(props);
}
handleChange = (e)=>{
// setValues({ ...values, [e.target.name]: e.target.value });
}
onSubmit = (formProps)=>{
debugger
}
render(){
const { handleSubmit } = this.props;
return(
<form noValidate onSubmit={handleSubmit(this.onSubmit)}>
<div>
<renderTextField
name="firstName"
label="First Name"
onChange={this.handleChange}
/>
</div>
<Button variant="contained" color="primary" type="submit">
Login
</Button>
</form>
)
}
}
const mapStateToProps = (state)=>{
return {
loading : state.loginReducer.loading,
error : state.loginReducer.error
}
}
export default connect(mapStateToProps,{login})(reduxForm({
form: 'LoginForm',
keepDirtyOnReinitialize: true,
enableReinitialize: true,
})(Login))
For some reason, I can't understand redirect is not working.
I don't get any error.
The Redirect is called from react-router-dom library
in the console I see the xxx.
** added **
for some reason the redirect returns an error
this is the redirect block:
redirectUser = (json) => {
console.log('json', json);
console.log('state', this.state);
if(this.state.auth == true){
localStorage.setItem('userid', json.userid);
localStorage.setItem('auth', this.state.auth);
console.log('xxx');
return <Redirect to='/biblist' />
}
}
this is the file data from chrome dev sources:
this is the entire component.
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux';
import { Redirect, Link } from 'react-router-dom';
import {
Button,
Form,
FormGroup,
FormControl,
Col,
Alert,
Checkbox,
ControlLabel,
HelpBlock,
Grid,
Row
} from 'react-bootstrap';
import { userLogedIn } from '../../actions';
class LoginForm extends Component {
constructor() {
super();
this.state={
username: '',
password: '',
data: [],
auth: false,
usernameError: '',
passwordError: '',
EmptyUsernameError: '',
EmptyPasswordError: ''
}
this.onSubmitLogin = this.onSubmitLogin.bind(this);
this.onChange = this.onChange.bind(this);
}
componentWillMount(){
console.log('mount', this.props);
}
componentDidUpdate(){
console.log('Update', this.props);
console.log('Update username', this.props.userid);
}
clientValidate = () => {
let isError = false;
if(this.state.username === ''){
isError = true;
this.setState({EmptyUsernameError: 'לא הזנתם שם משתמש'});
}
if(this.state.password === ''){
isError = true;
this.setState({EmptyPasswordError: 'לא הזנתם סיסמה'});
}
return isError;
}
validate = () => {
let isError = false;
if(this.state.auth === false){
isError = true;
this.setState({usernameError: 'אחד הפרטים שהזנתם שגוי'});
}
return isError;
}
onSubmitLogin(event){
event.preventDefault();
if(this.clientValidate()){
this.clientValidate();
}else{
fetch('http://127.0.0.1/bibli/api/user_switch/' + this.state.username +
'/'+ this.state.password )
.then(response => response.json())
.then(json => {
if(json.count > 0)
{
this.setState({auth: true});
this.props.userLogedIn(json);
this.redirectUser(json);
}else{
let isError = true;
this.validate();
this.setState({
auth: false,
data: null
});
}
})
.catch(error => console.log('parsing faild', error))
}
}
redirectUser = (json) => {
console.log('json', json);
console.log('state', this.state);
if(this.state.auth == true){
localStorage.setItem('userid', json.userid);
localStorage.setItem('auth', this.state.auth);
console.log('xxx');
debugger
return <Redirect to='/biblist' />
}
}
onChange(event){
this.setState({
[event.target.name]: event.target.value,
auth: false,
usernameError: '',
EmptyPasswordError: '',
EmptyUsernameError: ''
})
}
render() {
return (
<Grid fluid id="LoginForm" className="yellow-bg">
<Row className="show-grid">
<Col xsOffset={2} xs={8} mdOffset={3} md={6}>
<h2 className="text-center">כבר רשומים? התחברו!</h2>
<Form horizontal>
<FormGroup controlId="formHorizontalusername">
<Col xs={12} sm={4}>
<FormControl ref="username" name="username" type="text" onChange={this.onChange} placeholder="הקלד דואר אלקטרוני"/>
</Col>
<Col xs={12} sm={4}>
<FormControl ref="password" name="password" type="password" onChange={this.onChange} placeholder="הקלד סיסמא"/>
</Col>
<Col xs={12} sm={4} >
<Button onClick={this.onSubmitLogin} type="submit" className="full-width-btn" id="loginSubmit">התחבר</Button>
</Col>
</FormGroup>
{
this.state.EmptyUsernameError ?
<Alert variant="danger"> {this.state.EmptyUsernameError} </Alert> :
''
}
{
this.state.EmptyPasswordError ?
<Alert variant="danger"> {this.state.EmptyPasswordError} </Alert> :
''
}
{
this.state.usernameError ?
<Alert variant="danger"> {this.state.usernameError} </Alert> :
''
}
<small><Link to="/register">אינך רשום? הרשם!</Link></small>
</Form>
</Col>
</Row>
</Grid>
);
}
}
const mapDispatchToProps = dispatch => {
return {
userLogedIn: (params) => dispatch(userLogedIn(params))
};
};
const mapStateToProps = state => {
return {
userid: state.authReducer.userid,
auth: state.authReducer.auth
}
}
export default connect(mapStateToProps, mapDispatchToProps)(LoginForm);
You could try with "withRouter" HOC
Example :
import { withRouter } from 'react-router-dom'
const Button = ({ history }) => (
<button
type='button'
onClick={() => { history.push('/new-location') }}
>
Click Me!
</button>
)
export default withRouter(Button)
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.