Why is this not scoped in React form onSubmit function? - reactjs

In my React component I have a form with onSubmit function
<form className="form-horizontal" name="taskForm" onSubmit={this.submitTask}>
submitTask(e) {
e.preventDefault();
console.log(this.props); // undefined
console.log(this) // window object
}
For some reason this.props is not in scope when I use form onSubmit. When I console.log(this.props) in the constructor, props logs out normally.
When I console.log(this) it is the window object. How do I get the scope of the react component?

This is more broad problem, because similar behavior with this you will notice when you use other component events for example (onClick, onChange, onSubmit)
In documentation there is note about it:
https://facebook.github.io/react/docs/reusable-components.html#no-autobinding
Methods follow the same semantics as regular ES6 classes, meaning that they don't automatically bind this to the instance. You'll have to explicitly use .bind(this) or arrow functions =>.
As it is described you have to bind those methods or use arrow functions.
If you choose binding, then you can bind in constructor or strictly in rendered component.
In constructor:
constructor(props) {
super(props);
this.submitTask = this.submitTask.bind(this);
}
In rendered component:
<form className="form-horizontal" name="taskForm" onSubmit={this.submitTask.bind(this)}>
With arrow function you can pass submitTask content to arrow function:
<form className="form-horizontal" name="taskForm" onSubmit={e => {
e.preventDefault();
console.log(this.props); // undefined
console.log(this) // window object
}}>

Related

this undefined in arrow function

I am creating a component that contains of a Form with a Submit button. In the OnSubmit of the Form I call an arrow function. Inside this function I call the object "this" but I get the error message that it is undefined. However, if I just do a console.log(this) in the OnSubmit of the form instead of calling the arrow function, this is defined. Anyone knows how to solve this issue ? I am actually following a react/ethereum course and even though the code of the instructor works in his video, his code doesn't work when I use it.
import React, {Component} from 'react';
import { Button, Form, Input, Message } from 'semantic-ui-react'
class CampaignNew extends Component{
state = {
minimumContribution: "",
errorMessage: ""
}
onSubmit = async (event) => {
event.preventDefault();
this.setState({errorMessage: "test error"});
}
render(){
return (
<div>
<Form onSubmit={this.onSubmit} error={this.state.errorMessage}>
<Form.Field>
<label>Minimum contribution</label>
<Input
labelPosition="right"
value={this.state.minimumContribution}
onChange={event => this.setState({minimumContribution: event.target.value})}
style={{width: "250px"}} placeholder='0' />
</Form.Field>
<Button type='submit' primary>Create</Button>
</Form>
</div>
)
}
}
export default CampaignNew;
Your code runs fine for me, but there is some missing information on how you are creating your component. It seems you may not be using Create-React-App. Your issue is with method binding in a class component.
The React documentation (https://reactjs.org/docs/handling-events.html) states:
You have to be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound by default. If you forget to bind this.handleClick and pass it to onClick, this will be undefined when the function is actually called.
This is not React-specific behavior; it is a part of how functions work in JavaScript. Generally, if you refer to a method without () after it, such as onClick={this.handleClick}, you should bind that method.
If calling bind annoys you, there are two ways you can get around this. If you are using the experimental public class fields syntax, you can use class fields to correctly bind callbacks:
class LoggingButton extends React.Component {
// This syntax ensures `this` is bound within handleClick.
// Warning: this is *experimental* syntax.
handleClick = () => { console.log('this is:', this); }
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
This syntax is enabled by default in Create React App.
Also the error prop of the Form component should be a boolean and the async word is not required in the code you provided.
That's related to how this value is defined in javaScript. You should know that "this" value is defined by who calls it in runtime.
In Class, "this" value is the same this value of its parent. In this code, "this" value is CampaignNew class object.
class CampaignNew extends Component{
onSubmit = async (event) => {
event.preventDefault();
this.setState({errorMessage: "test error"});
}
But "this" value is different in code below.
<Form onSubmit={this.onSubmit}
This is because Form element calls onSubmit which means this value in onSubmit is the same value of Form element's "this" and Form element doesn't have this value so this is why the value's undefined.
I found the problem. It was my version of NextJS. If I downgrade it to an earlier version, everything works fine. No idea why it breaks in the new version.
Working version: "next": "^11.1.3"

How to explicitly create an optional property in a React component when it is not specificied?

In a React component I need a ref to the HTML element I use for some manual processing (e.g. to listen to resize events). I can do that by defining a ref member in my component and set that in the render method:
render() {
return <div ref={this.myRef} />;
}
However, sometimes the owner of this component also needs this ref for other work (e.g. dynamic handling of certain other events). It makes sense to use the same ref here, so I added a property to the component:
render() {
const { innerRef } = this.props;
return <div ref={innerRef} />;
}
The problem is now that the parent component not always uses the inner ref, so it doesn't specify it when creating the inner component. In order to maintain the functionality of the inner component I want to explicitly create the ref if not given by the parent like:
public constructor(props: MyProps) {
super(props);
if (!props.innerRef) {
props.innerRef = React.createRef<HTMLElement>();
}
}
However, that's not accepted because props is read only. What other approach could I take to accomplish this explicit init, if not provided?
Note: I tried to use the static defaultProps to provide such a default, but defaultProps is used for all instances of the class, so they all share the same default ref then. Hence this is not a good approach.
Instead of trying to directly assign a new value to props, check if the innerRef is available from the props and if not pass an internally created ref to the div like this.
<div ref={innerRef || internallyCreatedRef} />

What's the difference between using a function to return an object in setState and using an object directly?

In the following, the handleChange method throws an error when, inside setState, I use a function to return an object, but works fine when I update inputvalue directly as an object
class App extends React.Component {
constructor (props) {
super(props);
this.state = {
inputvalue: ''
}
this.handleChange = this.handleChange.bind(this);
}
handleChange (event) {
event.preventDefault();
this.setState(() => {
return {inputvalue: event.target.elements.name.value}//throws an error but works fine if I use just use object with out using a functon to return
}
);
}
render() {
return (
<div>
<form onSubmit={this.handleChange}>
<label>Name </label>
<input type="text" name="option" />
<button>submit</button>
</form>
<p> {this.state.inputvalue}</p>
</div>
);
}
}
render(<App />, document.getElementById('app'));
Warning that happens:
Warning: This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the method stopPropagation on a released/nullified synthetic event. This is a no-op function. If you must keep the original synthetic event around, use event.persist(). See https://reactjs.org/docs/events.html for more information.
Based on this:
the SyntheticEvent is pooled. This means that the SyntheticEvent
object will be reused and all properties will be nullified after the
event callback has been invoked. This is for performance reasons. As
such, you cannot access the event in an asynchronous way.
If you want to access the event properties in an asynchronous way, you
should call event.persist() on the event, which will remove the
synthetic event from the pool and allow references to the event to be
retained by user code.
You should call persist method when using a function as updater.
handleChange (event) {
event.persist();
this.setState(() => {
...
}
}
Take a look at this answer:
TypeError: evt.target is null in functional setState
i think u are accessing here wrong:
event.target.elements.name.value
should be
event.target.elements.option.value // because u set the name attribute to option (name="option") on the input element
and if i don't mistake the error is that u trying to access null reference right?

How to Call multiple Functions inside a Render in React/Jsx

I am trying to return multiple functions inside render, but its not getting call from another component:---
class OptionPanel extends React.Component {
constructor(props){
super(props);
this.onRemove = this.onRemove.bind(this);
this.onArrowUp = this.onArrowUp.bind(this);
this.onArrowDown = this.onArrowDown.bind(this);
}
onRemove(event){
event.preventDefault();
this.props.dispatch.deleteElement(this.props.DesignPanel.selectedElemId);
{/*event.currentTarget.parentElement.parentElement.parentElement.remove();*/}
}
onArrowUp(event){
event.preventDefault();
this.props.dispatch.moveElementUp(this.props.DesignPanel.selectedElemId);
}
render(){
return(
<div>
{this.onRemove()}
{this.onArrowUp()}
</div>
)
} }
Would this be the correct way of calling a function inside a render method?
What you're doing in your code-snipped is, that you're directly calling the methods (onRemove() and onArrowUp(). They will be called and whatever they return will be rendered as well.
So:
Would this be the correct way of calling a function inside a render method?
Yes, this is the right way, however it only makes sense, if these functions will return any content, that should be rendered (like additional components).
If you want to bind these functions to certain events, you have to put the functions as attributes to components.
For a button it would be:
<button type="button" onClick={this.onClickHandle()}>Click me</button>
So whenever the button will be clicked, the method "onClickHandle" will be called.
You don't need to extend React.Component if you only want to export functions from it
You can create a separate file action.js
and have
export function onRemove(event, dispatch, DesignPanel){
event.preventDefault();
dispatch.deleteElement(DesignPanel.selectedElemId);
}
export function onArrowUp(event, dispatch, DesignPanel){
event.preventDefault();
dispatch.moveElementUp(DesignPanel.selectedElemId);
}
and then import it in the component from where you need to call them like
import {onArrowUp, onRemove} from './path/to/action.js'
You miss binding the event handlers to html tags events
f.e
If you want to bind it to onClick and keyUp events
render(){
return(
<div>
<button onClick="{this.onRemove()}">
<input keyUp="{this.onArrowUp()}">
</div>
)
}
}

Is it possible to call dom element methods like focus() without using a ref? (Stateless Component Functions)

I try to use refs as least often as possible, but it seems there's no way around it for calling native dom methods (focus(), reset(), blur() etc) so I'm okay with that. Except, that I'd like to use the new stateless component functions for such basic things as form inputs, which as it stands, these stateless components do not allow refs pointing to them (they will return null). I understand that I can add a regular React class component wrapper around the stateless component to allow for ReactDOM.findDOMNode(ref) to work correctly, but what's the point of having the stateless function if it always has to be wrapped? Am I missing something?
Here's a solution that I came up with that doesn't require wrapping the stateless component in a class. Instead it involves the parent passing a function to the stateless component as a prop that is used as the callback function for the ref on the DOM element.
First set a method on the stateful parent that will be used as a callback to the ref, and another method for doing the action on the DOM element (in this case focusing after a key press). Then send the method to the stateless child as a prop.
// note: Facebook now recommends using native js classes instead of React.createClass().
// note 2: You may need Babel to transpile some of ES6 syntax in use here.
const StatefulParent = React.createClass({
getInitialState() {
return {
// doesn't matter for this example
}
},
componentDidMount() {
document.addEventListener('keyup', this.keyUp, false)
},
keyUp() {
// calls the DOM focus method on the input when the 'tab' key is pressed
if (e.keyCode === 9) this._input.focus()
},
inputSetter(ref) {
this._input = ref
},
render() {
<StatelessTextInput {...this.state} refCallback={this.inputSetter} />
}
})
The stateless component assumes a method will be passed to it from the parent called refCallback. This prop can be passed down any number of component generations to reach the DOM element.
const StatelessTextInput = ({refCallback, ...props}) => (
<input {...props} ref={refCallback} />
)
Just wrap your stateless component with class component (es6) an attach ref. It's written in the official docs
The cleanest solution I found is by setting a local variable and then assigning ref to it. Then this can be accessed and focused when needed:
const Item = () => {
let input;
const click = () => {
if (input){
input.focus();
}
};
return (
<div>
<a href="#" onClick={click}>clickMe</a>
<input ref={e => (input = e)}/>
</div>
);
};
yes, just remember it during rendering and carry it out, and focus on it after rendered:
function Main(props) {
<input ref={e => { props.elems.input = e; }}
}
const elems = {};
ReactDOM.render(Main({ elems }), containerElement);
elems.input.focus();
Just had this problem too with a stateless component. In at least React v16, you can get a reference to the element like this:
<Button
onClick={(el) => {
el.target.blur();
}}>
Button Text
</Button>

Resources