i am trying to create a login page in react using antd. I found a tutorial for doing this,but i belive that it is outdated, because it gives an error. I read on a forum that form.create() is not available anymore, but i don't know how to replace it.Here is my code:
import { Form, Icon, Input, Button, Spin } from 'antd';
import { connect } from 'react-redux';
import { NavLink } from 'react-router-dom';
import * as actions from '../store/actions/auth';
const FormItem = Form.Item;
const antIcon = <Icon type="loading" style={{ fontSize: 24 }} spin />;
class NormalLoginForm extends React.Component {
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
this.props.onAuth(values.userName, values.password);
this.props.history.push('/');
}
});
}
render() {
let errorMessage = null;
if (this.props.error) {
errorMessage = (
<p>{this.props.error.message}</p>
);
}
const { getFieldDecorator } = this.props.form;
return (
<div>
{errorMessage}
{
this.props.loading ?
<Spin indicator={antIcon} />
:
<Form onSubmit={this.handleSubmit} className="login-form">
<FormItem>
{getFieldDecorator('userName', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" />
)}
</FormItem>
<FormItem>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }],
})(
<Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" />
)}
</FormItem>
<FormItem>
<Button type="primary" htmlType="submit" style={{marginRight: '10px'}}>
Login
</Button>
Or
<NavLink
style={{marginRight: '10px'}}
to='/signup/'> signup
</NavLink>
</FormItem>
</Form>
}
</div>
);
}
}
const WrappedNormalLoginForm = Form.create()(NormalLoginForm);
const mapStateToProps = (state) => {
return {
loading: state.loading,
error: state.error
}
}
const mapDispatchToProps = dispatch => {
return {
onAuth: (username, password) => dispatch(actions.authLogin(username, password))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(WrappedNormalLoginForm);
When i try to run it, i get an error that says:
TypeError: _antd.Form.create(...) is not a function
How can i rewrite the code in order to make it run?
AntD has removed Form.create in v4 and you can replace the above code with the modified API structure like
class NormalLoginForm extends React.Component {
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
this.props.onAuth(values.userName, values.password);
this.props.history.push('/');
}
});
}
render() {
let errorMessage = null;
if (this.props.error) {
errorMessage = (
<p>{this.props.error.message}</p>
);
}
const { getFieldDecorator } = this.props.form;
return (
<div>
{errorMessage}
{
this.props.loading ?
<Spin indicator={antIcon} />
:
<Form onSubmit={this.handleSubmit} className="login-form">
<FormItem name='userName' rules={[{ required: true, message: 'Please input your username!' }]}>
<Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" />
</FormItem>
<FormItem name="password" rules={[{ required: true, message: 'Please input your Password!' }]}>
<Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" />
</FormItem>
<FormItem>
<Button type="primary" htmlType="submit" style={{marginRight: '10px'}}>
Login
</Button>
Or
<NavLink
style={{marginRight: '10px'}}
to='/signup/'> signup
</NavLink>
</FormItem>
</Form>
}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
loading: state.loading,
error: state.error
}
}
const mapDispatchToProps = dispatch => {
return {
onAuth: (username, password) => dispatch(actions.authLogin(username, password))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(NormalLoginForm);
Please see the migration guidelines for more details
I think you are using Antd v4.*. Form.create({}) is no more in latest version, so you have to your code as follow to make it working. V4 has. major changes so you have to go through migration guidelines
import { Form, Input, Button, Spin } from 'antd';
import { LoadingOutlined, UserOutlined, LockOutlined } from "#ant-design/icons";
import { connect } from 'react-redux';
import { NavLink } from 'react-router-dom';
import * as actions from '../store/actions/auth';
const FormItem = Form.Item;
const antIcon = <LoadingOutlined style={{ fontSize: 24 }} />;
class NormalLoginForm extends React.Component {
handleFinish = (values) => {
this.props.onAuth(values.userName, values.password);
this.props.history.push('/');
}
render() {
let errorMessage = null;
if (this.props.error) {
errorMessage = (
<p>{this.props.error.message}</p>
);
}
return (
<div>
{errorMessage}
{
this.props.loading ?
<Spin indicator={antIcon} />
:
<Form onFinish={this.handleFinish} className="login-form">
<FormItem name="userName" rules={[{ required: true, message: 'Please input your username!' }]}>
<Input prefix={<UserOutlined style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" />
)}
</FormItem name="password" rules={[{ required: true, message: 'Please input your Password!' }]}>
<FormItem>
<Input prefix={<LockOutlined style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" />
)}
</FormItem>
<FormItem>
<Button type="primary" htmlType="submit" style={{marginRight: '10px'}}>
Login
</Button>
Or
<NavLink
style={{marginRight: '10px'}}
to='/signup/'> signup
</NavLink>
</FormItem>
</Form>
}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
loading: state.loading,
error: state.error
}
}
const mapDispatchToProps = dispatch => {
return {
onAuth: (username, password) => dispatch(actions.authLogin(username, password))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(NormalLoginForm);
Related
I am using Ant Design Steps for my form but I am having a problem on how to save the value of the previous input.
Here is my code whole code:
import React from 'react';
import 'antd/dist/antd.css';
import './index.css';
import { Form, Icon, Input, Button, Steps } from 'antd';
const { Step } = Steps;
const steps = [
{
title: 'First',
content: 'First-content',
},
{
title: 'Second',
content: 'Second-content',
}
];
class NormalLoginForm extends React.Component {
constructor (props) {
super (props) ;
this.state = {
current: 0
}
}
next() {
const current = this.state.current + 1;
this.setState({ current });
}
prev() {
const current = this.state.current - 1;
this.setState({ current });
}
handleSubmit = e => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
};
render() {
const { current } = this.state
const { getFieldDecorator } = this.props.form;
return (
<div>
<Steps current={current}>
{steps.map(item => (
<Step key={item.title} title={item.title} />
))}
</Steps>
<Form onSubmit={this.handleSubmit} className="login-form">
{current === 0 ? (
<Form.Item>
{getFieldDecorator('username', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<Input
prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
placeholder="Username"
/>,
)}
</Form.Item>
): ''}
{current === 1 ? (
<Form.Item>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }],
})(
<Input
prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />}
type="password"
placeholder="Password"
/>,
)}
</Form.Item>
): ''}
</Form>
{current < steps.length - 1 && (
<Button type="primary" onClick={() => this.next()}>
Next
</Button>
)}
{current === steps.length - 1 && (
<Button type="primary" onClick={this.handleSubmit}>
Done
</Button>
)}
{current > 0 && (
<Button style={{ marginLeft: 8 }} onClick={() => this.prev()}>
Previous
</Button>
)}
</div>
);
}
}
What is happening is when I click next, the value of the previous input is gone. What I want to happen is when I click next the previous value will be save so when I click submit both of the values will be output in the console. The code comes from ant design and I used it for example
You should consider storing the data in a state :
constructor (props) {
super (props) ;
this.state = {
current: 0,
firstInput: '',
secondInput: '',
}
}
<Input
prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
placeholder="Username"
onChange={(e) => this.setState({firstInput: e.target.value})}
/>,
Then, in your submit press, you will be able to access the values of firstInput and secondInput by doing this.state.firstInput & this.state.secondInput.
I am trying to make a simple form.
I am able to get all valid data if they are inputted and receive errors on the console, if they are not:
async-validator: ["username is required"]
async-validator: ["passwordis required"]
But the errors are not rendering.
Parent
const HomePage = () => (
<Layout className="layout" style={{ minHeight: '100vh' }}>
<Header>Teste</Header>
<Content className={_s.Content}>
<LoginForm />
</Content>
<Footer style={{ textAlign: 'center' }}>React Node Boilerplate by Igor Cesar</Footer>
</Layout>
);
export default HomePage;
Form
class LoginForm extends React.Component {
handleSubmit = e => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
};
render() {
const { getFieldDecorator } = this.props.form;
return (
<Form onSubmit={this.handleSubmit} className={_s.loginForm}>
<Form.Item>
{getFieldDecorator('username', {
rules: [{ required: true, message: 'Please input your username!' }]
})(
<Input
prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
placeholder="Username"
/>
)}
</Form.Item>
<Form.Item>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }]
})(
<Input
prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />}
type="password"
placeholder="Password"
/>
)}
</Form.Item>
<Form.Item>
{getFieldDecorator('remember', {
valuePropName: 'checked',
initialValue: true
})(<Checkbox>Remember me</Checkbox>)}
<a className={_s.loginFormForgot} href="/">
Forgot password
</a>
<Button type="primary" htmlType="submit" className={_s.loginFormButton}>
Log in
</Button>
Or register now!
</Form.Item>
</Form>
);
}
}
const WrapperLoginForm = Form.create()(LoginForm);
export default WrapperLoginForm;
In order to help people who will reach this page in the future:
The author reported the bug to antdesign and a solution was found.
This was a problem with lodash-webpack-plugin, and the required change is in webpack.config.js. Turns out LodashModuleReplacementPlugin() should receive {paths: true} as a parameter, and the problem's gone.
module.exports = {
//... rest of your webpack config
plugins: [
//... rest of your plugins
new LodashModuleReplacementPlugin({paths: true}),
],
}
I am completely new to both react and testing and am a bit stumped.
I was just wondering if someone could tell me why my test fails. I assume I making a basic mistake in how this should work.
I am trying to test a log in page. At the moment I am just trying to get my test to fire an onclick from a button and check that that a function has been called.
The log in component code can be seen below.
import React, { Component, Fragment } from "react";
import { Redirect } from "react-router-dom";
// Resources
//import logo from "assets/img/white-logo.png";
//import "./Login.css";
// Material UI
import {
withStyles,
MuiThemeProvider,
createMuiTheme
} from "#material-ui/core/styles";
import Button from "#material-ui/core/Button";
import TextField from "#material-ui/core/TextField";
import Person from "#material-ui/icons/Person";
import InputAdornment from "#material-ui/core/InputAdornment";
// Custom Components
import Loading from "components/Loading/Loading.jsx";
// bootstrat 1.0
import { Alert, Row } from "react-bootstrap";
// MUI Icons
import LockOpen from "#material-ui/icons/LockOpen";
// remove
import axios from "axios";
// API
import api2 from "../../helpers/api2";
const styles = theme => ({
icon: {
color: "#fff"
},
cssUnderline: {
color: "#fff",
borderBottom: "#fff",
borderBottomColor: "#fff",
"&:after": {
borderBottomColor: "#fff",
borderBottom: "#fff"
},
"&:before": {
borderBottomColor: "#fff",
borderBottom: "#fff"
}
}
});
const theme = createMuiTheme({
palette: {
primary: { main: "#fff" }
}
});
class Login extends Component {
constructor(props, context) {
super(props, context);
this.state = {
username: "",
password: "",
isAuthenticated: false,
error: false,
toggle: true,
// loading
loading: false
};
}
openLoading = () => {
this.setState({ loading: true });
};
stopLoading = () => {
this.setState({ loading: false });
};
toggleMode = () => {
this.setState({ toggle: !this.state.toggle });
};
handleReset = e => {
const { username } = this.state;
this.openLoading();
api2
.post("auth/admin/forgotPassword", { email: username })
.then(resp => {
this.stopLoading();
console.log(resp);
})
.catch(error => {
this.stopLoading();
console.error(error);
});
};
handleSubmit = event => {
event.preventDefault();
localStorage.clear();
const cred = {
username: this.state.username,
password: this.state.password
};
api2
.post("auth/admin", cred)
.then(resp => {
console.log(resp);
localStorage.setItem("api_key", resp.data.api_key);
localStorage.setItem("username", cred.username);
return this.setState({ isAuthenticated: true });
})
.catch(error => {
if (error.response) {
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
console.log(error.request);
} else {
console.log("Error", error.message);
}
console.log(error.config);
return this.setState({ error: true });
});
};
handleInputChange = event => {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
};
forgotPassword = () => {
console.log("object");
};
render() {
const { error } = this.state;
const { isAuthenticated } = this.state;
const { classes } = this.props;
if (isAuthenticated) {
return <Redirect to="/home/dashboard" />;
}
return (
<div className="login-page">
<video autoPlay muted loop id="myVideo">
<source
src=""
type="video/mp4"
/>
</video>
<div className="videoOver" />
<div className="midl">
<Row className="d-flex justify-content-center">
<img src={''} className="Login-logo" alt="logo" />
</Row>
<br />
<Row className="d-flex justify-content-center">
{error && (
<Alert style={{ color: "#fff" }}>
The username/password entered is incorrect. Try again!
</Alert>
)}
</Row>
<MuiThemeProvider theme={theme}>
<Row className="d-flex justify-content-center">
<TextField
id="input-username"
name="username"
type="text"
label="username"
value={this.state.username}
onChange={this.handleInputChange}
InputProps={{
className: classes.icon,
startAdornment: (
<InputAdornment position="start">
<Person className={classes.icon} />
</InputAdornment>
)
}}
/>
</Row>
{this.state.toggle ? (
<Fragment>
<br />
<Row className="d-flex justify-content-center">
<TextField
id="input-password"
name="password"
type="password"
label="pasword"
value={this.state.password}
onChange={this.handleInputChange}
className={classes.cssUnderline}
InputProps={{
className: classes.icon,
startAdornment: (
<InputAdornment position="start">
<LockOpen className={classes.icon} />
</InputAdornment>
)
}}
/>
</Row>
</Fragment>
) : (
""
)}
</MuiThemeProvider>
<br />
<Row className="d-flex justify-content-center">
{this.state.toggle ? (
<Button
className="button login-button"
data-testid='submit'
type="submit"
variant="contained"
color="primary"
onClick={this.handleSubmit}
name = "logIn"
>
Login
</Button>
) : (
<Button
className="button login-button"
type="submit"
variant="contained"
color="primary"
onClick={this.handleReset}
>
Reset
</Button>
)}
</Row>
<Row className="d-flex justify-content-center">
<p onClick={this.toggleMode} className="text-link">
{this.state.toggle ? "Forgot password?" : "Login"}
</p>
</Row>
</div>
<Loading open={this.state.loading} onClose={this.handleClose} />
</div>
);
}
}
export default withStyles(styles)(Login);
My current test which fails.
import React from 'react'
import {render, fireEvent, getByTestId} from 'react-testing-library'
import Login from './login'
describe('<MyComponent />', () => {
it('Function should be called once', () => {
const functionCalled = jest.fn()
const {getByTestId} = render(<Login handleSubmit={functionCalled} />)
const button = getByTestId('submit');
fireEvent.click(button);
expect(functionCalled).toHaveBeenCalledTimes(1)
});
});
I'm also fairly new to React Testing Library, but it looks like your button is using this.handleSubmit, so passing your mock function as a prop won't do anything. If you were to pass handleSubmit in from some container, then I believe your tests, as you currently have them, would pass.
I am using react-cookie in my project. When i use withCookies() i am getting warning in console log like this
Warning: Failed prop type: Invalid prop component of type object
supplied to Route, expected function
How can i solve this.?
Below is the code
import React from 'react';
import { Form, Icon, Input, Button, Checkbox } from 'antd';
import { withCookies } from "react-cookie";
import './Login.css';
class NormalLoginForm extends React.Component {
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
console.log(values);
const { cookies } = this.props;
cookies.set('token', 'dfsfsd54dg2g45fg575f432sd4');
}
});
}
render() {
const { getFieldDecorator } = this.props.form;
return (
<div style={{ textAlign: 'center' }}>
<img className="logo-white" src={'https://cdn.medcampus.io/wp-content/uploads/2018/08/01131559/MC_Logo_Black.png'} alt="logo"/>
<div className="login-container">
<br/>
<Form onSubmit={this.handleSubmit} className="login-form">
<Form.Item>
{getFieldDecorator('email', {
rules: [{ required: true, message: 'Please input your Email id!' }, {
type: 'email', message: 'The input is not valid E-mail!',
}],
})(
<Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Email" size={"large"} />
)}
</Form.Item>
<Form.Item>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }],
})(
<Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" size={"large"} placeholder="Password" />
)}
</Form.Item>
<Form.Item>
{getFieldDecorator('remember', {
valuePropName: 'checked',
initialValue: true,
})(
<Checkbox>Remember me</Checkbox>
)}
<a className="login-form-forgot" href="">Forgot password</a>
<Button type="primary" size={"large"} htmlType="submit" className="login-form-button">Log in</Button>
</Form.Item>
</Form>
</div>
</div>
);
}
}
const Login = withCookies(Form.create({ name: 'normal_login' })(NormalLoginForm));
export { Login };
Following the comments, you are exporting all imports from your containers' files as default, so basically you'll get an object, not a function(the exact export).
To prevent this, there is several ways to do it:
Import your container in App from its own file.
in index.js of pages, you can do this :
import { Login } from `LoginContainerFile`;
import { Comp1} from `Comp1ContainerFile`;
import { Comp2} from `Comp2ContainerFile`;
export { Login, Comp1, Comp2 };
Good luck and hope it resolves your problem.
Below is my loginForm.js from where I suspect the error is originating:-
import { Form, Icon, Input, Button, Checkbox } from 'antd';
const FormItem = Form.Item;
import React, { Component } from 'react';
class NormalLoginForm extends Component {
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
}
render() {
const { getFieldDecorator } = this.props.form;
return (
<Form onSubmit={this.handleSubmit} className="login-form">
<FormItem>
{getFieldDecorator('userName', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" />
)}
</FormItem>
<FormItem>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }],
})(
<Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" />
)}
</FormItem>
<FormItem>
<Button type="primary" htmlType="submit" className="login-form-button">
Log in
</Button>
Or register now!
</FormItem>
</Form>
);
}
}
const WrappedNormalLoginForm = Form.create()(NormalLoginForm);
export default WrappedNormalLoginForm;
Below is my main component file where I load handle routing:-
import React, { Component } from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import WrappedNormalLoginForm from './loginForm'
export default class App extends Component {
render() {
return (
<div className="container">
<BrowserRouter>
<div>
<Route exact path="/" component={WrappedNormalLoginForm} />
</div>
</BrowserRouter>
</div>
);
}
}
I am unable to resolve this issue. I have extended using capital C which was mentioned as a fix for many of the questions asked regarding this.