I have a user delete form, which leads users to create some tickets which will be discussed inside the back-end, but as I can see that after creating a ticket I would like to change some return pattern, instead of the form I would like to return a simple text, but I cannot get my this.props.user inside componentDidMount, I grab the state from redux. This is my code:
import React from 'react';
import { connect } from 'react-redux';
import { MdDeleteSweep } from 'react-icons/md';
import {
Container,
Title,
Span,
DangerSpan,
Desc,
Form,
FieldLabel,
TextField,
Field,
Btn,
} from './elements/Delete';
import { delete_request } from '../../actions/auth';
class AccountDelete extends React.Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
desc: '',
ticketCreated: false,
};
this.onChange = this.onChange.bind(this);
this.handleClick = this.handleClick.bind(this);
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
handleClick(e) {
e.preventDefault();
this.props.delete_request(this.state.email);
this.setState({ ticketCreated: true });
}
componentDidMount() {
console.log(this.props);
if (!this.props.user) return null;
let delres = this.props.user.del_request[0];
if (delres) {
this.setState({ ticketCreated: true });
}
}
render() {
const { user } = this.props;
if (!user) return null;
return (
<Container>
<div style={{ marginBottom: '1rem' }}>
<Title>
<MdDeleteSweep /> <DangerSpan>Delete</DangerSpan> Your Account
</Title>
<Desc>
You can submit your Winteka Account for deletion at any time. If you
change your mind, you might not be able to recover it.
</Desc>
</div>
{this.state.ticketCreated ? (
<Desc>
<DangerSpan>*</DangerSpan> You have already <b>opened a request</b>,
please check your <b>e-mail address</b> for more information. For
any questions please <b>Contact Us</b>.
</Desc>
) : (
<Form>
<FieldLabel>
<DangerSpan>*</DangerSpan> Your <Span>active email address</Span>
</FieldLabel>
<Field
type="email"
name="email"
placeholder={user.email}
value={this.state.email}
onChange={this.onChange}
/>
<FieldLabel>
<DangerSpan>*</DangerSpan> Your <Span>current password</Span>
</FieldLabel>
<Field
type="password"
name="password"
value={this.state.password}
onChange={this.onChange}
/>
<FieldLabel>
Describe <Span>your problem</Span>
</FieldLabel>
<TextField
rows="3"
name="desc"
value={this.state.desc}
onChange={this.onChange}
/>
<Btn onClick={this.handleClick}>Submit Application</Btn>
</Form>
)}
</Container>
);
}
}
const mapStateToProps = (state) => ({
user: state.auth.user,
});
export default connect(mapStateToProps, { delete_request })(AccountDelete);
this is what this.props is in componentDidMount
1. Why do you need the this.props.user inside componentDidMount ?
It seems like you are unnecessary duplicating the this.setState({ ticketCreated: true }) call inside both componentDidMount and
handleClick but it should be enough to just put it inside
handleClick so you can just remove the componentDidMount block.
If you want to introduce a side-effect or execute a callback
when ticketCreated becomes true, you should either,
a) use the componentDidUpdate lifecycle hook or,
b) this.setState(newState, callbackFn) where callbackFn gets executed on the state update.
2. The error you are getting,
Warning: Failed propType: Invalid prop component supplied to Route
indicates you are on an old version (<4.4.0) of react-router-dom. Link to relevant github issue.
You should probably update the package with the following command,
$ npm install --save react-router-dom
Related
I have a form in a react app that allows the user to edit something they posted and I want to set the default input's value as the old publication (stored in State) so that they can change it in real life.
For example, the user created a note and then they want to change a word in that, not the whole note.
Hey, this is my old note to Hey, this is my new note
This is the form, it works perfectly except for the input value.
(the state is set by props that come from another component and it has nothing to do with the problem, I think)
import React, {Component} from 'react';
import { connect } from 'react-redux';
import {editNote} from "../actions/noteActions"
class EditNote extends React.Component{
constructor(props){
super(props);
this.onChangeTitle = this.onChangeTitle.bind(this);
this.onChangeBody = this.onChangeBody.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
id: this.props.note[0],
newNote:{
title : this.props.note[1],
body : this.props.note[2]
},
error : ""
}
}
onChangeTitle(e){
const newTitle = e.target.value;
this.setState(prevState => ({
...prevState,
newNote: {
...prevState.newNote,
title: newTitle
}
}))
}
onChangeBody(e){
const newBody = e.target.value;
this.setState(prevState => ({
...prevState,
newNote: {
...prevState.newNote,
body: newBody
}
}))
}
onSubmit(e){
e.preventDefault();
this.props.editNote(this.state.id, this.state.newNote);
this.props.history.push("/");
}
render(){
return(
<div>
<h1>Edit your note</h1>
<form onSubmit={this.onSubmit}>
<label>Title</label>
<input onChange={this.onChangeTitle} name="title" type="text" />
<label>Note</label>
<input onChange={this.onChangeBody} name="body" type="text" />
<button>Update changes!</button>
</form>
</div>
)
}
}
const mapDispatchToProps = (dispatch) => ({
editNote: (id, updates) => dispatch(editNote(id, updates))
});
export default connect(null, mapDispatchToProps)(EditNote);
I can set the input's value with this.state but then it doesn't allow me to change any of it, and I can't find another way to do it.
<input onChange={this.onChangeTitle} name="title" type="text" value={this.state.newNote.title}/>
As shown in description, I am working on a React project and utilizing Firebase for Auth and popup is not firing but I think it might have to do with this warning from React. The warning I am getting from React is that Warning: Expected onClick listener to be a function, instead got a value of object type.
I have been researching this and trying to problem solve for 2 days. Any pointers would be greatly appreciated :) Thank you!!
Here is my Firebase Utils code for the auth:
const config = {
My config info
};
firebase.initializeApp(config);
const provider = new firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({ prompt: 'select_account' });
export const signInWithGoogle = () => auth.signInWithPopup(provider)
.then(function (result) {
}).catch(error => {
console.log(error);
})
export default firebase;
Now my Custom Button Component in React 17
import React from 'react';
import './custom-button.styles.scss';
const CustomButton = ({children, isGoogleSignIn, ...otherProps}) => (
<button
className={`${isGoogleSignIn ? 'google-sign-in' : ''} custom-button`}
{...otherProps}>
{children}
</button>
);
export default CustomButton;
Lastly my signin component in React
lass SignIn extends React.Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: ''
}
}
handleSubmit = event => {
event.preventDefault();
this.setState({ email: '', password: '' })
}
handleChange = event => {
const { value, name } = event.target;
this.setState({ [name]: value })
}
render() {
return (
<div className='sign-in'>
<h2>I already have an account</h2>
<span>Sign in with your email and password</span>
<form onSubmit={this.handleSubmit}>
<FormInput
name='email'
type='email'
handleChange={this.handleChange}
value={this.state.email}
label='email'
required
/>
<FormInput
name='password'
type='password'
value={this.state.password}
handleChange={this.handleChange}
label='password'
required
/>
<div className='buttons'>
<CustomButton type='submit'> Sign in </CustomButton>
<CustomButton onClick={signInWithGoogle} isGoogleSignIn>
Sign in with Google
</CustomButton>
</div>
</form>
</div>
);
}
}
export default SignIn;
I finally figured this out. The firebase config file should have had a .then and .catch for the promise. Once I added this, it works fine now. I researched 3 days to figure this out. Taking a course, and the guy who produced this course didn't add this, although it was two years ago maybe it wasn't required then, but it is now.
Background
I have a react/redux project with a form. I'd like it to redirect to another page on my site after the form is submitted.
In researching the issue here, I tried this and this and this, plus other solutions on github and none quite work. Most solutions dealt with a state change, some used withRouter. I'm not sure what I am missing.
Code
Here is my form.
import React from 'react'
import {connect} from 'react-redux'
import {addProgram} from '../../actions/addProgram'
import {Form} from 'semantic-ui-react'
import {Redirect} from 'react-router'
class ProgramInput extends React.Component {
constructor(props){
super(props)
this.state = {
name: '',
network: '',
image: '',
fireRedirect: false
}
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange = event => {
this.setState({
[event.target.name]: event.target.value
})
}
handleRedirect = event => {
this.setState({
fireRedirect: true
})
}
handleSubmit = event => {
event.preventDefault()
this.props.addProgram(this.state)
this.setState({
name: '',
network: '',
image: '',
fireRedirect: ''
})
}
render(){
const fireRedirect = this.state.fireRedirect
if (fireRedirect === true) {
return <Redirect to='/programs' /> }
return(
<Form onSubmit={this.handleSubmit}>
<h2>Create a New Show</h2>
<Form.Input
fluid
label='Name'
name='name'
value={this.state.name}
onChange={this.handleChange}
width={6} />
<Form.Input
fluid
name='network'
label='Network'
value={this.state.network}
onChange={this.handleChange}
width={6} />
<Form.Input
fluid
name='image'
label='Image Link'
value={this.state.image}
onChange={this.handleChange}
width={8} />
<Form.Button>Submit</Form.Button>
</Form>
)
}
}
export default connect(null, {addProgram})(ProgramInput)
Thank you very much for your time!
The reason your current code fails is because handleRedirect is never called. It's unnecessary to have this in it's own method, but if that's what you prefer, then just call handleRedirect() from handleChange.
You never call your handleRedirect function to update the fireRedirect state. Try modifying your handleSubmit function, and use fireRedirect in the setState call-back. This will give the visual effect of redirecting to "/programs" after submitting and clearing the form.
handleSubmit = event => {
event.preventDefault()
this.props.addProgram(this.state)
this.setState({
name: '',
network: '',
image: '',
fireRedirect: ''
}, () => this.handleRedirect())
}
This question already has answers here:
How to disable button in React.js
(8 answers)
Closed 3 years ago.
I am using trying to disable a button in react based on couple states. Down below is a breakdown of my code
constructor(props) {
super(props);
this.state = {
email: '',
pass: '',
disabled: true
}
this.handleChange = this.handleChange.bind(this);
this.handlePass = this.handlePass.bind(this);
}
pretty self explanatory constructor. The disabled will be changed as state changes. My render method looks something like this
render() {
if(this.state.email && this.state.pass) {
this.setState({ disabled: false })
}
return (
<div className='container'>
<div className='top'></div>
<div className='card'>
<MuiThemeProvider>
<Card >
<div className='wrapper'>
<TextField
hintText="Email"
value={this.state.email} onChange={this.handleChange}
/><br/>
<TextField
hintText="Password"
type="password"
/><br/>
<div className='login-btn'>
<RaisedButton label="Login" primary={true}
disabled={this.state.disabled} />
</div>
</div>
</Card>
</MuiThemeProvider>
</div>
</div>
)
}
As you can see I have 2 text fields and I am handeling the data changes with the following method
handleChange(e) {
this.setState({email: e.target.value});
}
handlePass(e) {
this.setState({pass: e.target.value});
}
Now my button is initially disabled and everytime a state is changed and component re-renders I want to check for state changes and enable button accordingly. So I was thinking of using the life cycle method like so
componentWillMount() {
if(this.state.pass && this.state.disabled) {
this.setState({disabled: false})
}
}
However, this doesn't work. When both email and password field is not empty the button stays disabled. I am not sure what am I doing wrong.
Please, do not set states inside render() function. That might cause infinite loops to occur.
Refer: https://github.com/facebook/react/issues/5591
Instead of setting states inside render() function, you can set the disabled state inside the handleChange() and handlePass() function.
If more detail required, please do mention.
You should be setting the disabled state inside your handleChange and handlePass functions.
componentWillMount() only runs right before the component is rendered, but never again.
Just made a demo , is that you need, check the code in the demo below
demo
Change below code :
class App extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
pass: '',
invalidData: true
}
this.onEmailChange = this.onEmailChange.bind(this);
this.onPasswordChange = this.onPasswordChange.bind(this);
}
// componentWillUpdate is to be deprecated
//componentWillUpdate(nextProps, nextState) {
// nextState.invalidData = !(nextState.email && nextState.pass);
//}
onEmailChange(event) {
this.setState({ email: event.target.value });
}
onPasswordChange(event) {
this.setState({ pass: event.target.value });
}
render() {
return (
<form>
<input value={this.state.email} onChange={this.onEmailChange} placeholder="Email" />
<input value={this.state.password} onChange={this.onPasswordChange} placeholder="Password" />
// from this <button disabled={this.state.invalidData}>Submit</button>
//to
<button disabled={!(this.state.email && this.state.password)}>Submit</button>
</form>
);
}
}
**updated **
disable submit button in <button disabled={!(this.state.email && this.state.password)}>Submit</button> itself.
Consider having form component like:
export default class Form extends React.Component {
constructor (props) {
this.state = { email: '' }
this.onChange = this.onChange.bind(this)
}
onChange(event) {
this.setState({ email: event.target.value })
}
render () {
return (
<div>
<h2>{this.props.title}</h2>
<form className={cx('Form')} onSubmit={this.props.onSubmit}>
<input className={cx('Form-email')} type='email' placeholder='email' value={this.state.email} onChange={this.onChange} />
<input className={cx('Form-btn')} type='submit' value='sign up' />
</form>
</div>
)
}
}
I would then use this <Form onSubmit={this.someFunction} /> component elsewhere within my app, lets assume inside HomePage component. Inside that home page I would have this.someFunction that executes when form is summited, how can I pass form value / state to it?
Create a callback in your component that will call the function sent to Form with the state as parameter.
export default class Form extends React.Component {
constructor (props) {
this.state = { email: '' }
this.onChange = this.onChange.bind(this)
this.onSubmit = this.onSubmit.bind(this)
}
onChange(event) {
this.setState({ email: event.target.value })
}
onSubmit() {
this.props.onSubmit(this.state);
}
render () {
return (
<div>
<h2>{this.props.title}</h2>
<form className={cx('Form')} onSubmit={this.onSubmit}>
<input className={cx('Form-email')} type='email' placeholder='email' value={this.state.email} onChange={this.onChange} />
<input className={cx('Form-btn')} type='submit' value='sign up' />
</form>
</div>
)
}
}
What you're (essentially) looking to do is pass some data up the component chain (to a parent component). You could implement this with vanilla React, but I'm not going to advise you to do this.
If you try implementing some kind of state management yourself, unless your app is incredibly simple or you are an incredibly disciplined one-man-team, it's likely to get messy and unpredictable fast.
I advocate one way data flow. Data should flow one way through your app - down. I recommend you look at implementing a solution with Flux or Redux (Redux is my preference). These are both state containers that will propagate state throughout your app and enforce a set of conventions which you help you maintain structure to your data flow as your app grows.
I admit, you're adding to the learning curve by implementing a solution with these containers, but remember that React is only the view layer and it can't help you much with problems surrounding state management.
You could do this:
export default class Form extends React.Component {
constructor (props) {
this.state = { email: '' }
this.onChange = this.onChange.bind(this)
this.onSubmit = this.onSubmit.bind(this)
}
onChange(event) {
this.setState({ email: event.target.value })
}
// Wrap around this.props.onSubmit and add data to it.
onSubmit() {
this.props.onSubmit(this.state);
}
render () {
return (
<div>
<h2>{this.props.title}</h2>
<form className={cx('Form')} onSubmit={this.onSubmit}>
<input className={cx('Form-email')} type='email' placeholder='email' value={this.state.email} onChange={this.onChange} />
<input className={cx('Form-btn')} type='submit' value='sign up' />
</form>
</div>
)
}
}
Very similar to how you bound and use your onChange.