I am using react with antd framework. I have created a form and handling the submit:
My forms.js:
import React from 'react';
import { Form, Input, Button, notification } from 'antd';
import axios from 'axios';
class CustomForm extends React.Component {
handleFormSubmit = (event, requestType, articleId) => {
const title = event.target.elements.title.value;
const content = event.target.elements.content.value;
notification.open({
message: 'Success',
description:
'Your Response is submitted',
onClick: () => {
console.log('Notification Clicked!');
},
onCancel: () => {
}
});
// eslint-disable-next-line
switch (requestType) {
case 'post':
return axios.post('http://localhost:8000/api/', {
title:title,
content:content
}
)
.then (res => console.log(res))
.catch(err => console.error(err))
case 'put':
return axios.put(`http://localhost:8000/api/${articleId}/`, {
title:title,
content:content
})
.then (res => console.log(res))
.catch(err => console.error(err))
}
}
render(){
return (
<div>
<Form onSubmitCapture={(event) => this.handleFormSubmit(event, this.props.requestType, this.props.articleId)} >
<Form.Item label="Title">
<Input name='title' placeholder="Input some title" />
</Form.Item>
<Form.Item label="Content">
<Input name='content' placeholder="Input some content" />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType='submit' >{this.props.btntext}</Button>
</Form.Item>
</Form>
</div>
)
}
};
export default CustomForm;
Using this I can get input from the user and show a success notification. But I want to reload my page after 5 seconds when I get the notification when I try to use this code window.location.reload(true) in my handleFormSubmit it is not allowing the notification to take place.
You can use setTimeout(() => window.location.reload(true), 5000); this code
Related
Below are 2 files that is expected to display details in a form, name, service, date, cost. The problem is that it doesn't display information entered in other input fields when I choose a future date. Whereas when I use the current date, it displays the information entered in other input fields as expected. Why is this the case please and how do i fix it?
import { useState, useEffect } from 'react';
import axios from 'axios';
const ConfirmBooking = () => {
//track state
const [data,setData] = useState([])
const Style = {
color: 'rgb(97, 113, 154)',
padding: '5px'
}
//GET data
useEffect(() => {
axios
.get('http://localhost:5000/api/bookings')
.then(res => {
console.log(res)
setData(res.data)
})
.catch(err => {
console.log(err)
})
}, [])
//DELETE data
const deleteHandler =(id) =>{
axios
.delete('http://localhost:5000/api/bookings/'+id)
.then(res => {
console.log(res.data);
}
)
.catch(error =>{
console.log(error)
})
}
if(!data?.length) return <div>loading...</div>
return (
<div className='bookings'>
<h4 style={Style}>Name:{" "}{data.at(-1).name}</h4>
<h4 style={Style} >Service:{" "}{data.at(-1).service}</h4>
<h4 style={Style} >Date:{" "}{data.at(-1).date}</h4>
<h4 style={Style} >Cost:{" "}{data.at(-1).cost}</h4><br></br>
<button className='Btn'>Edit</button>
<button className='Btn' onClick={ () => deleteHandler(data.at(-1))} >Delete</button>
</div>
)
}
export default ConfirmBooking;
import React, { useState } from 'react';
import axios from 'axios';
import { useNavigate } from 'react-router-dom'
import DatePicker from 'react-datepicker';
import "react-datepicker/dist/react-datepicker.css";
const Form = () => {
const navigate = useNavigate();
const [myState, setMyState] = useState({
name: "",
service: "finance",
date: new Date(),
cost: "3$"
});
//event to handle all inputs except datepicker
const handleChange = (e)=> {
// const { name, value} = e.target;
const name = e.target.name;
const value = e.target.value
//to update the input myState
setMyState
({...myState, [name]: value });
}
const handleDateChange = (date) => {
setMyState({
date:date
})
}
const handleSubmit = (e) => {
e.preventDefault();
if (myState !== "") {
alert('booking success')
}
//Add data to database
axios.post('http://localhost:5000/api/bookings', myState)
.then(res => {
setMyState
(res.data);
console.log(res);
//redirect to another page
navigate('/ConfirmBooking')
})
.catch((error) => {
console.log(error)
})
}
return (
<form className='form' onSubmit={handleSubmit} >
<h2 className="headerForm">Create appointment</h2>
<div className="mb-3">
<label className="form-label">Name</label>
<input name='name' type="text" className="form-control" id="exampleFormControlInput1" value={myState.name} onChange={handleChange} />
<label className="form-label">Service</label>
<input name='service' type="text" className="form-control " id="exampleFormControlInput1" value={myState.service} onChange={handleChange} />
<label className="form-label"> Date</label>
<div>
<DatePicker
selected={myState.date}
onChange={handleDateChange}
startDate = {new Date()}
minDate={new Date()}
filterDate={date => date.getDay() !== 6 && date.getDay() !== 0}
/>
</div>
<label className="form-label">Cost</label>
<input name='cost' type="text" className="form-control" id="exampleFormControlInput1" value={myState.cost} onChange={handleChange} />
</div>
<button >Submit</button>
</form>
)
}
export default Form;
This method is changing the state without other properties:
...
const handleDateChange = (date) => {
setMyState({
date:date
})
}
...
If you want to change this property from state, you need to destructure previous value and change date.
const handleDateChange = (date) => {
setMyState({
...myState,
date:date
})
}
I want to display a loader till my chart is displayed.I have implemented the following code but it doesn't work.The loader should display as soon as "compare" button is clicked:
import React, { Component, Fragment,useState,useRef,createRef} from "react";
import Loader from "react-loader-spinner";
import RestAPI from "services/api";
import axios from "axios";
import Select from 'react-select';
import "d3-transition";
import {
Button,
Label,
FormGroup,
Form,
} from "reactstrap";
import ReactApexChart from "react-apexcharts";
import "tippy.js/dist/tippy.css";
import "tippy.js/animations/scale.css";
class StackedAreaChart extends Component {
constructor(props) {
super(props);
this.selectInputRef = React.createRef();
this.selectInputRef1 = React.createRef();
this.selectValue=this.selectValue.bind(this)
this.clickEvent=this.clickEvent.bind(this)
this.handleChange=this.handleChange.bind(this)
this.onClear=this.onClear.bind(this)
this.checkIfMounted=this.checkIfMounted.bind(this)
this.state = {
soptions:[],
weights:[],
years:[],
loader:false,
selectOptions: [],
selectedValues:[],
combinedList:[],
yearsList:[],
isLoaded:false,
selectValue:"",
series: []
};
}
checkIfMounted() {
console.log("mount")
return this.selectInputRef1.current != null;
}
componentDidMount(){
fetch("http://127.0.0.1:8000/api/getallvals/")
.then(response => response.json())
.then(json => {
this.setState({
soptions:json.topics,
})
});
}
handleChange=(e)=>{
const value = Array.isArray(e) ? e.map(s => s.value) : []
this.state.selectedValues=value
console.log("value is:",this.state.selectedValues)
}
selectValue(e){
var selectValue=this.state.selectValue;
var isDisplayed=this.state.isDisplayed;
console.log("the val is:",e);
console.log("the val is:",e.length);
console.log("the val is:",e[0].value);
this.setState({
selectedValues:e,
loader:false
});
console.log(this.state.selectedValues)
}
onClear() {
//window.location.reload(false);
//console.log(this.selectInputRef1.current)
this.selectInputRef.current.select.clearValue();
}
//this.selectInputRef1.current.chart.destroy();}
clickEvent(){
console.log("in click event",this.state.selectedValues)
var selectedValues=this.state.selectedValues;
var {series}=this.state;
fetch("http://127.0.0.1:8000/api/getallvals/"+'?'+selectedValues.join('&'))
.then(response => response.json())
.then(json => {
series=[]
for(let i=0;i<json.weights.length;i++){
series=series.concat([{name:selectedValues[i],data:json.weights[i]}])
//selectInputRef1.current.chart.publicMethods.updateOptions({})
}
this.setState({
series: series,
datalabels:{
enabled:true
},
options:{
stroke:{
curve:'smooth'
},
xaxis:{
categories:json.years
},
},
isLoaded:true,
loader:false
})
});
console.log("this state",series)
}
render() {
const checkmount=this.checkIfMounted;
var {options,isLoaded,loader,series,yeardata,soptions,weights,years}=this.state;
if(isLoaded){
return (
<div id="chart" className="box" >
<Form role="form" method="POST" >
<FormGroup>
<h2>Evolution of a value over Time
</h2>
<Label>Select a value</Label>
<Select ref={this.selectInputRef} name="selectOptions" isClearable isMulti placeholder="Select Option" options={soptions}
onChange={this.handleChange}/>
<br></br>
<Button onClick={ this.clickEvent }
>Compare</Button>
<Button onClick={this.onClear}>Reset</Button>
<Loader type="puff" visible={loader} color="#00BFFF" height={100} width={100}></Loader>
</FormGroup>
</Form>
{checkmount?(
<ReactApexChart ref={this.selectInputRef1} options={this.state.options} series={this.state.series} type="area" height={350} />):
(<div>not yet loaded</div>)}
</div>)
}
else{
return (
<div id="chart">
<Form role="form" method="POST" >
<FormGroup>
<h2>Evolution of a values over time
</h2>
<Label>Select a value</Label>
<br></br>
<Select ref={this.selectInputRef} name="selectOptions" isClearable isMulti placeholder="Select Option" options={soptions}
onChange={this.handleChange}/>
<br></br>
<Button onClick={this.clickEvent}>Compare</Button>
<Button onClick={this.onClear}>Reset</Button>
<Loader type="puff" visible={false} color="#00BFFF" height={100} width={100}></Loader>
</FormGroup>
</Form>
</div>)
}
}
}
export default StackedAreaChart;
loader is the flag I have kept to decide whether or not to display loader but it still doesn't show up.Also I am disabling the loader when the page is not loaded
So what you want to do is, while you are fetching the data, the loader will be true. Once you fetch the data, the loader will be false.
So, inside clickEvent you can change
fetch("http://127.0.0.1:8000/api/getallvals/"+'?'+selectedValues.join('&'))
.then(response => response.json())
.then(json => {
to this:
this.setState({loader: true})
fetch("http://127.0.0.1:8000/api/getallvals/"+'?'+selectedValues.join('&'))
.then(response => {
this.setState({loader: false})
response.json()
})
.then(json => {
...
React apexcharts has a mounted event that you can use, it is part of the chart-options:
https://apexcharts.com/docs/options/chart/events/
You could have your own isLoaded state set to false initially, and when the apex-chart mounted event is fired you set your isLoaded state to true.
This way you can chose to display the loader when isLoaded is false, and hide it when it turns to true.
I have a component that lists the current todo's which you can add your own todo's and delete todo's, which is working 100%. The only issue I am facing is updating my current todo's. I have added the necessary code below, help would be appreciated.
I am using proptypes and can see I have warning in my console which I suspect might be the problem. Here is the warning:
Warning: Failed prop type: The prop `editTodo` is marked as required in `TodoItem`, but its value is `undefined`.
in TodoItem (at Todos.js:10)
in Todos (at App.js:83)
in section (at App.js:82)
in Route (at App.js:75)
in main (at App.js:73)
in div (at App.js:72)
in Router (created by BrowserRouter)
in BrowserRouter (at App.js:71)
in App (at src/index.js:7)
Here is my edit todo button:
<div id="card-item-edit">
<button
className="edit-btn"
onClick={() => this.toggleForm()}>
EDIT
</button>
{this.showEditTodoForm()}
</div>
In my edit todo button I have assigned a toggle function onClick which opens input field if true:
toggleForm = () => {
if (!this.state.isShowing) {
this.setState({ isShowing: true });
} else {
this.setState({ isShowing: false });
}
}
Which passes state and opens this form onClick
showEditTodoForm = () => {
if(this.state.isShowing) {
return(
<div>
<form onSubmit={this.handleFormSubmit}>
<input
type="text"
name="edit todo"
placeholder="Edit Your Todo"
value={this.state.value}
onChange={this.onChange}
/>
</form>
</div>
)
}
}
onSubmit the value is updated with Axios. I think I might be doing something wrong here, I tried testing with Postman but just can't get it working, here is my handleFormSubmit function:
handleFormSubmit = (id) => {
const title = this.state.title;
event.preventDefault();
axios.put(`http://localhost:3004/todos/${id}`,
{
title
},
)
.then(() => {
})
.catch(error => console.log(error))
}
I am also using the onChange property in the form submit, here is the function:
onChange = (e) =>
this.setState({
[e.target.name]: e.target.value // demo react tools to show what happens when value changes when typing
}
);
Managed to resolve this with online mentor from Codementor, would highly recommend this resource for anyone stuck with problems like these. Here is the solution:
Edit todo form, using ref's to pass state:
showEditTodoForm = () => {
const { title} = this.props.todo
if(this.state.isShowing) {
return(
<div>
<form ref={this.formRef} onSubmit={this.handleFormSubmit}>
<input
type="text"
name="title"
placeholder="Edit Your Todo"
defaultValue={title}
/>
<input type="submit" value="Save" />
</form>
</div>
)
}
}
handleFormSubmit = (e) => {
e.preventDefault()
const title = this.formRef.current['title'].value
const { id } = this.props.todo
this.props.editTodo(id, title)
}
Then I am using proptypes to pass to my main component:
editTodo = (id, title) => {
axios.put(`http://localhost:3004/todos/${id}`,
{
title
},
)
.then(({data}) => {
this.setState(prevSate => {
const { todos } = prevSate;
const oldTodoIndex = todos.findIndex(todo => todo.id === data.id )
const newTodo = {...todos[oldTodoIndex], ...data}
todos.splice(oldTodoIndex, 1, newTodo)
return {todos: todos}
})
})
.catch(error => console.log(error))
}
I'm trying to create a change password page in react and i'm getting typeError: cannot read property 'users' of null. The code works for other form pages(where i'm doing PUT and CREATE) but not this one
I tried binding the submit handler to the this keyword but that didn't work.
Also tried binding the handlePasswordChange to the this keyword
./formchange
import React from "react";
import { Link } from "react-router-dom";
var createReactClass = require("create-react-class");
var FormChange = createReactClass({
//setting initial state
getInitialState() {
return {
password: {}
};
},
handlePasswordChange(e) {
this.setState({
password: e.target.value
});
},
handleSubmit(e) {
e.preventDefault();
this.props.onSubmit(this.state);
this.props.history.push("/");
},
render() {
return (
<form
name="categories_post"
className="form-horizontal"
onSubmit={this.handleSubmit}
>
<div id="change_password">
<div className="form-group">
<label
className="col-sm-2 control-label required"
htmlFor="password"
>
Password
</label>
<div className="col-sm-10">
<input
type="text"
value={this.state.password}
onChange={this.handlePasswordChange}
id="password"
className="form-control"
/>
</div>
</div>
<button
type="submit"
id="formChangeSubmit"
className="btn btn-default"
>
Submit
</button>
</div>
</form>
);
}
});
export default FormChange;
./passwordupdate
import React from "react";
import { updateUsers, fetchUsers } from "./actions/appactions";
import FormChange from "./formchange";
var createReactClass = require("create-react-class");
const Update = createReactClass({
getIntitialState() {
return {
users: {}
};
},
componentWillReceiveProps(props) {
this.setState(props);
},
componentDidMount() {
fetchUsers(this.props.match.params.usersId)
.then(data => {
this.setState(state => {
state.users = data;
return state;
});
})
.catch(err => {
console.error("error", err);
});
},
handleSubmit(data) {
updateUsers(this.state.users.id, data);
},
render() {
return (
<div>
<FormChange
onSubmit={this.handleSubmit.bind}
password={this.state.users.password}
/>
</div>
);
}
});
export default Update;
//fetchusers function
export function fetchUsers(id) {
return fetch("https://localhost:44341/api/users/" + id, {
method: "GET",
mode: "cors"
})
.then(res => res.json())
.catch(err => err);
}
<FormChange
onSubmit={this.handleSubmit.bind(this)}
password={this.state.users.password}
/>
make this change and check
I'm not sure but you have data in handleSubmit as parameter but you don't pass it.
try this
You can call function like this:
handleSubmit=(data)=> {
updateUsers(this.state.users.id, data);
},
and call it
onSubmit={(data)=> this.handleSubmit(data)}
The problem was in ComponentDidMount(). The state was always null, had to change it to this
componentDidMount() {
fetchTheUsers(this.props.match.params.usersId)
.then(data => {
this.setState({
users: data
});
})
I did it that way initially because that's how it worked for my other update files. Hope this is useful to someone else.
I'm struggling to understand where is the best practice to handle success submit and navigate to the next page.
I'm working on a login form:
class LogInComponent extends Component {
render() {
const {dispatch} = this.props;
const {loginError, handleSubmit, pristine, reset, submitting} = this.props;
return (
<form onSubmit={handleSubmit((values) => dispatch(login(values))) }>
<Field name="username" type="text" component={renderField} label="Username" />
<Field name="password" type="password" component={renderField} label="Password" />
{loginError && <strong>{loginError}</strong>}
<div>
<button type="submit" disabled={submitting}>Log In</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>Clear Values</button>
</div>
</form>
)
}
And the action:
export function login(values) {
const email = values.username;
const password = values.password;
return dispatch => {
dispatch(loginSubmit());
firebaseAuth.signInWithEmailAndPassword(email, password).then(function(user) {
dispatch(signInSuccess(user));
}).catch(function(error) {
dispatch(signInError(error));
});
};
}
export function loginSubmit() {
return {
type: SIGN_IN_SUBMIT
};
}
export function signInError(error) {
return {
type: SIGN_IN_ERROR,
payload: error.message
};
}
export function signInSuccess(user) {
return {
type: SIGN_IN_SUCCESS,
payload: user
};
}
If the response was successful, I would like to navigate to the next page. But where should the navigation be? not from the reducer or action, so only from component, but the action does not return response by design..
Am I missing something?
Create a composing function to couple your login code and navigation logic, and dispatch that function on form submit.
Modify the actions file as below:
import { browserHistory } from './react-router';
// no export needed, this is a #private function
function login(values) {
const email = values.username;
const password = values.password;
return dispatch => {
dispatch(loginSubmit());
return firebaseAuth.signInWithEmailAndPassword(email, password)
.then((user) => dispatch(signInSuccess(user)))
.catch((error) => dispatch(signInError(error)));
};
}
export function loginAndRedirect(loginParams) {
return dispatch => dispatch(login(loginParams))
.then(() => browserHistory.push('/success/path'))
.catch(() => browserHistory.push('/failure/path'));
}
// ... other actions
Now in our component we will do this:
<form onSubmit={handleSubmit((values) => dispatch(loginAndRedirect(values))) }>