this undefined in arrow function - reactjs

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"

Related

Update parent state variable from child React Reactive Form

I am trying to explore react library with next framework. Since I am an angular developer, I like to reuse some of my reactive-form to my new application. I found this library since it has almost the same implementation of reactive-form.
Now, I am using state variable on my parent form; however, whenever I try to update the value from child (which is the reactive form). I cannot accomplish it.
Here's my simple code to replicate this.
import React, { useState } from "react";
import { FieldGroup, FieldControl } from "react-reactive-form";
export default function App() {
const [isRegistered, setIsRegistered] = useState(false);
async function submitForm(e) {
e.preventDefault();
setIsRegistered(state => !state);
console.log(isRegistered);
//await function call .....
}
return (
<div>
<h1>Hello StackBlitz!</h1>
<FieldGroup
control={form}
render={({ get, invalid }) => (
<form onSubmit={submitForm}>
<FieldControl
name="email"
render={TextInput}
meta={{ label: "Email" }}
/>
<button type="submit">Submit</button>
<p>{isRegistered.toString()}</p>
{isRegistered ? <span>Registered Successfully</span> : null}
</form>
)}
/>
</div>
)
}
Just to keep it short, the form and TextInput is just a model and an element.
As you can see., I am updating my state variable on the submitForm function by putting it as an onSubmit function of my form; however, I am able to trigger the submitForm whenever I am trying to submit, but the state variable value doesn't change.
The thing is, when I try to call the submitForm function outside the child (FieldGroup), I am able to update the value.
I created a sample app so you can check as well.
It seems like you need to set strict prop to false for FieldGroup, like described here: https://github.com/bietkul/react-reactive-form/blob/master/docs/api/FieldGroup.md
strict: boolean;
Default value: true
If true then it'll only re-render the component only when any change happens in the form group control irrespective of the parent component(state and props) changes.
I don't know this library, but to me it just looks like the FormGroup is not re-render, because none of it's props are being changed.
The documentation says that passing strict={false} to the <FieldGroup /> component should allow it to re-render when the parent component updates as well. In your given example (thanks for making an example) that also does the trick.

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>
)
}
}

Access to internal function from externally loaded HTML in React component

I have the following use case.
Some HTML from a third party source is loaded inside my React component:
class MyComponent extends Component {
render() {
return (
<div
dangerouslySetInnerHTML={{ __html: this.props.externalHTML }}
/>
);
}
}
Inside the externally loaded HTML a click event exists for a specific span, which is supposed to call a callback function that exists in my application.
<span onclick="myCallback(param1='asd', param2=123, param3='asdas')">
Click me!
</span>
Where should I put this myCallback function?
If I place it inside the component class I get the following error when clicking the span, because as I understand the function is not visible to the externally loaded HTML: Uncaught ReferenceError: myCallback is not defined at HTMLSpanElement.onclick
My other idea was to add the function to the window object window.myCallback = ... inside my main index.js file to be loaded every time the app loads. This way it works but I have two issues.
My understanding is that this is not the correct React way to do it.
Whenever I click the span element the callback function is triggered twice and I cannot understand why.
Any suggestions?
Using "dangerouslySetInnerHTML" is ..."dangerous" as its name ^^, which is actually not pure React way, either.
However, If you have to do it, you can do something like this (take advantage of built-in jQuery inside React be default)
=====
EDITED VERSION FROM HERE: (use only 1 component)
export default class MyComponent extends Component {
componentDidMount() {
// using jQuery to manipulate DOM element form third-party source
// NB. you may think of using setTimeout here, to wait for the external source to be fully loaded here, of course it's not so safe
// but anyway, we are using "dangerouslySetInnerHTML" anyway => quite dangerous, though ^^
// setTimeout(function(){
$(document.findElementsByTagName("span")[0]).click(function(e){
// or perhaps $("#spanID").click if you can, just be careful between DOM element and react component
e.preventDefault();
// DO SOMETHING HERE, just like you do in the window.onload function
// or maybe you also need to get param values by getting $(this).data("...") or $(this).attr("ATTRIBUTE-NAME")
return false;
});
// });
}
render() {
return (
<div
dangerouslySetInnerHTML={{ __html: this.props.externalHTML }}
/>
);
}
}
=====
OLD ANSWER FROM HERE: (use 2 components)
ParentComponent:
export default class MyComponent extends Component {
constructor(props) {
super(props);
this.callbackOnThisComponent = this.callbackOnThisComponent.bind(this);
}
callbackOnThisComponent(param1, param2, param3) {
// do whatever you like with the above params
}
render() {
return (
<ChildComponent triggerCallbackOnParent={this.callbackOnThisComponent} />
);
}
}
ChildComponent:
export default class ChildComponent extends Component {
componentDidMount() {
// using jQuery to manipulate DOM element form third-party source
let that = this;
// NB. you may think of using setTimeout here, to wait for the external source to be fully loaded here, of course it's not so safe
// but anyway, we are using "dangerouslySetInnerHTML" anyway => quite dangerous, though ^^
$(document.findElementsByTagName("span")[0]).click(function(e){
// or perhaps $("#spanID").click if you can, just be careful between DOM element and react component
e.preventDefault();
that.props.triggerCallbackOnParent(param1, param2, param3);
// or maybe you need to get param values by getting $(this).data("...") or $(this).attr("ATTRIBUTE-NAME")
return false;
}, that);
}
render() {
return (
<div
dangerouslySetInnerHTML={{ __html: this.props.externalHTML }}
/>
);
}
}
I just use the main React's idea, which is passing props downward to children components, and when you want to trigger a function upward from child component, create a callback function on parent. For your or anyone else's reference, this is my demonstration on how to pass callback function from parent to multi-level-children components:
Force React container to refresh data
Re-initializing class on redirect
if this doesn't work yet, feel free to show me some error logs, thanks

Why is this not scoped in React form onSubmit function?

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
}}>

Resources