React app returning null on element? - javascript

I have a react modal that renders sign-in and sign-up form.
And on the form, there is a button by id of modal-sign-in-submit-button.
When I do
document.getElementById('modal-sign-in-submit-button');
It returns a type error, saying that such element does not exist...
TypeError: document.getElementById(...) is null
Is there a package or a workaround in making it to recognize the button being in the html?
This works fine on console, but when I try to access an element rendered by React in a different script, it does not find such element. (Probably due to it not being generated yet).
Below is the source for the part,
import React, { Component } from 'react';
import Modal from 'react-modal';
class SignInModalTrigger extends Component {
constructor() {
super();
this.state = {
open: false
};
this.openModal = this.openModal.bind(this);
this.closeModal = this.closeModal.bind(this);
}
openModal() {
this.setState({open: true});
}
closeModal() {
this.setState({open: false});
}
render () {
return (
<div>
<a className="navbar-items" onClick={this.openModal} href="#">Sign in</a>
<Modal className="sign-in-modal" isOpen={this.state.open}>
<h1 className="modal-title">Sign in</h1>
<div className="modal-inner-form">
<input className="modal-input" id="modal-sign-in-mail-input" type="email" placeholder="Enter your email here"/>
<br />
<br />
<input className="modal-input" id="modal-sign-in-password-input" type="password" placeholder="Enter your password here" pattern="(?=.*[A-Z]).{6,}"/>
<br />
<br />
<button className="modal-button" id="modal-sign-in-submit-button">Submit</button>
<br />
<br />
<button className="modal-button" onClick={this.closeModal}>Close</button>
</div>
</Modal>
</div>
);
}
}
export default SignInModalTrigger;

It doesn't look like you're creating a button with an id of 'submit-button'. You're searching for a button that doesn't exist. Try to enter the following:
document.getElementById('modal-sign-in-submit-button');
If you're looking for a framework that will allow you to identify elements on the DOM, I would recommend jQuery for dev purposes. Please note that you should try to avoid using jQuery as it is heavy and rather pointless.
I would also like to recommend that you try using lambda expressions or fat arrow functions for this.openModal & this.closeModal. This will allow you to keep the context of 'this' without the usage of .bind(). The obvious benefits are: improved code readability and the explicit binding of 'this'.
Here is an example:
openModal() => { this.setState({ open: true });
closeModal() => { this.setState({ open: false });
I hope that helps!

A noob question it was, but I found a solution in achieving what I wanted.
It's to create functions within the class itself, and then bind it to the constructor.
So something like,
constructor() {
super();
this.state = {
open: false
};
this.openModal = this.openModal.bind(this);
this.closeModal = this.closeModal.bind(this);
this.submit = this.submit.bind(this);
}
submit() {
const submit = document.getElementById('modal-sign-up-submit-button');
const email = document.getElementById('modal-sign-up-mail-input').value;
const pass = document.getElementById('modal-sign-up-password-input').value;
const promise = firebase.auth().createUserWithEmailAndPassword(email, pass);
promise.then(e => window.location.href="index.html");
promise.catch(e => console.log(e.message));
firebase.auth().onAuthStateChanged(firebaseUser => {
if(firebaseUser) {
this.setState({open: false});
console.log("hi");
}
});
}
render() {
return(
<button className="modal-button" id="modal-sign-up-submit-button" onClick={this.submit}>Submit</button>
)
}

Related

React: problem calling child function in parent component [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 1 year ago.
I made a games page in react. When you win one, it displays a form dialog (with material-UI: https://material-ui.com/components/dialogs/#form-dialogs). Component's visibility depends on the open attribute, which is changed with "handleClickOpen" when you push the button. I wanted to reuse code so I made a component that contains the dialog. Here is my code so far (child class):
class Pop_dialog extends Component {
constructor(props) {
super(props)
this.state = {
open: false
}
}
handleOpen() {
console.log('A')
this.setState({ open: true })
}
handleClose() {
console.log('B')
this.setState({ open: false })
}
render() {
return (
<Dialog open={this.state.open} onClose={this.handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Subscribe</DialogTitle>
<DialogContent>
<DialogContentText>
To subscribe to this website, please enter your email address here. We will send updates
occasionally.
</DialogContentText>
<TextField
autoFocus
margin="dense"
id="name"
label="Email Address"
type="email"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={this.handleClose} color="primary">
Cancel
</Button>
<Button onClick={this.handleClose} color="primary">
Subscribe
</Button>
</DialogActions>
</Dialog>
)
}
I call "handleOpen" within a function in the parent class:
triggerDialog() { this.dialogRef.current.handleOpen(); }
render ()
{
...
<Pop_dialog ref={this.dialogRef}/>
}
The triggerDialog function is called when I win/lost the game and it opens the form dialog fine. The problem comes when I try to close it (with the Cancel or Subscribe buttons). The page throws the next error:
I couldnĀ“t find why it fails here but not when it use "handleOpen". By the way, this is the 4th solution that i use. I also tried using a function component with the useContext hood (It didn't work at all), passing 'open' like a prop to the child (I also could open the dialog but not close it) and turn 'open' in a session var, defined in the parent component and called in the child (I couldn't open the dialog).
I don't know if some of this ideas would work or if I need a completely new one. I am open to any kind of idea, as long as it keeps Pop_dialog reusable.
It doesn't seem as though you've bound this to the handlers in Pop_dialog. The result is that this is undefined in the callback handlers.
Bind in the constructor:
class Pop_dialog extends Component {
constructor(props) {
super(props)
this.state = {
open: false
}
this.handleOpen = this.handleOpen.bind(this);
this.handleClose = this.handleClose.bind(this);
}
handleOpen() {
console.log('A')
this.setState({ open: true })
}
handleClose() {
console.log('B')
this.setState({ open: false })
}
...
Or convert the handlers to arrow functions so this of the class is bound automatically.
class Pop_dialog extends Component {
constructor(props) {
super(props)
this.state = {
open: false
}
}
handleOpen = () => {
console.log('A')
this.setState({ open: true })
}
handleClose = () => {
console.log('B')
this.setState({ open: false })
}
...

React.js this.props.user always is null in componentDidMount

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

React spread operator throwing error on nested state updatey

I am trying to update a nested state in my React login component to handle change to the login form fields.
Login Container Component
class Login extends Component {
constructor() {
super();
// define state
this.state = {
credentials: { 'username':'', 'password':'' },
redirect: false
};
// do the binding thing that React should really just do for you
this.handleChangeField = this.handleChangeField.bind(this);
this.httpRequest = this.httpRequest.bind(this);
this.renderRedirect = this.renderRedirect.bind(this);
}
handleChangeField(e) {
// perform coding acrobatics to update a nested state
const credentials = {...this.state.credentials} // !!! Unexpected token error
credentials[ e.target.name] = e.target.value;
this.setState({credentials})
}
renderRedirect(){
if(this.state.redirect) {
return <Redirect to='/' />
}
}
httpRequest(){
const self = this;
axios.post('/login/',this.state.credentials).then(function(res){
if(res.data.success){ // loggedIn = true;
self.setState({ redirect: true })
}
});
}
render() {
return (
<div>
{this.renderRedirect()}
<LoginForm
handleChangeField = {this.handleChangeField}
httpRequest = {this.httpRequest}
state = {this.state}
/>
</div>
)
}
}
Login Form Component
const LoginForm = ({handleChangeField, httpRequest, state}) => (
<div className="login-form block-center">
<div className="h4 mbot10">Login</div>
<div>
<input type="text" name="username" placeholder="username" value={state.credentials.username} onChange={handleChangeField} className="form-control" />
<input type="text" name="password" placeholder="password" value={state.credentials.password} onChange={handleChangeField} className="form-control" />
<button onClick={httpRequest} >Login</button>
</div>
</div>
)
It seems that the compiler is not recognizing the ... spread operator apparently need to update a nested state.
Many solutions Ive found use this exact syntax, what am I missing here?
Non-rhetorical side-question in the form of an editorial:
Why is it so goddamn hard to update a state for a nested object. Why would the people designing React ever assume that developers would use anything but a nested object for the state in most cases. I feel like React really needs to do a better job to support updating these kinds of operations. How about..
this.setState({credentials[e.target.name] : e.target.value})
Is that really so hard to implement?

How not to refresh the page after onClick

I have small class in react, i want to display the result on the screen after i click on the button, but before the display happens, the page reload.
how do i do it?
what am I missing?
import React, {Component} from 'react';
class InputFieldWithButton extends Component{
constructor(props){
super();
this.state = {
message: ''
};
}
handleChange(e){
this.setState({
message: e.target.value
});
}
doSomething(e){
return(
<h1>{this.state.message}</h1>
)
}
render(){
return (
<div>
<form >
<input type="text" placeholder="enter some text!" value=
{this.state.message}
onChange={this.handleChange.bind(this)}/>
<button onClick={this.doSomething.bind(this)}>Click me</button>
</form>
</div>
)
}
}
export default InputFieldWithButton;
Your button is inside a form and triggering a submit.
You can use the preventDefault() method to stop it from doing so:
doSomething(e) {
e.preventDefault();
return (
<h1>{this.state.message}</h1>
)
}
By the way, your return statement of this click handler makes no sense at the moment.
Edit
As a followup to your comment:
Can you explain me what is my mistake in the return?
Not really a mistake, but it is useless in this context as your are not doing anything with the returned object.
Where and how do you expect to use the <h1>{this.state.message}</h1> that you are returning?
If you intend to show / hide the input message in your screen you could do it with conditional rendering.
Just store a bool like showMessage in your state and render the message only if it's set to true.
Here is a small example:
class InputFieldWithButton extends React.Component {
constructor(props) {
super(props);
this.state = {
message: '',
showMessage: false
};
}
handleChange = (e) => {
this.setState({
message: e.target.value
});
}
toggleMessage = (e) => {
e.preventDefault();
this.setState({ showMessage: !this.state.showMessage })
}
render() {
const { showMessage, message } = this.state;
return (
<div>
<form >
<input
type="text"
placeholder="enter some text!"
value={message}
onChange={this.handleChange}
/>
<button onClick={this.toggleMessage}>Toggle Show Message</button>
{showMessage && <div>{message}</div>}
</form>
</div>
)
}
}
ReactDOM.render(<InputFieldWithButton />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
By the way, it is considered as bad practice to bind the functions inside the render method, because you are creating a new instance of a function on each render call. instead do it inside the constructor which will run only once:
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
Or you can use arrow functions which will reference this in a lexical context:
handleChange = (e) => {
this.setState({
message: e.target.value
});
}
This is what i've used in my example.
you're not specifying the buttons'type
<button type="button">
Set the type attribute on the button to be button. The default is submit since it is wrapped in a form. So your new button html should look like this:
<button type="button" onClick={this.doSomething.bind(this)}>Click me</button>

How to get state / value from form component?

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.

Categories