Arguments undefined after passing to React function? - javascript

In the Home component we want to call a function:
refreshMatchStatus = (match_id, status) => {
console.log(match_id)
var matches = this.state.matches
matches.map(function(match){
if (match.id == match_id){
match.challenged = status
}
})
this.setState({matches: matches})
}
From the Home render this function is passed down to the next component:
<List refreshMatchStatus={this.refreshMatchStatus.bind(this)} showAlert={this.props.showAlert} user={this.state.user} token={this.state.token} matches={this.state.matches}/>
The function is then passed down through some components like this:
refreshMatchStatus={this.props.refreshMatchStatus}
When it arrives in the ChallengePopup component it is executed like this:
this.props.refreshMatchStatus(match_id, status)
For some reason the arguments match_id and status are undefined when they are passed.
If we do a console.log in the ChallengePopup component, one line before the function is called, the match_id will return the right id number. But when we do a console.log on the first line of the refreshMatchStatus function, the match_id returns undefined.
We suspect this has something to do with the bind(this), but we cannot find any way to pass the arguments in the right way.

Move your bind call to the constructor e.g.
constructor(props) {
super(props);
this.refreshMatchStatus = this.refreshMatchStatus.bind(this);
}
Then you can remove the .bind call in your render method, otherwise since its the parent component, every time a fresh render is triggered the method binding is reset which I believe can result in loss of arguments from children.

Related

React onClick function triggering itself at mount

I have a component that contains a basic onClick function.
The function is called when the component is rendering (without any click), why is it happening?
import React, { Component } from "react";
class Some extends Component {
constructor(props) {
super(props);
}
someFunc = (message) => {
console.log(message)
}
render() {
return (
<p onClick={this.someFunc('hello')}>test</p>
)
}
}
in React you need to pass unexecuted functions, so either
onClick = {this.someFunction}
or if you need to pass an argument
onClick = {() => this.someFunction('argument')}
You don't have to append your function like that. You just need to call it by other ways:
<p onClick={() => this.someFunc('hello')}>test</p>
By using this syntax you can call it by params.
But your first solution was just passing the function
If your function had no argument to pass you could just pass the function like below:
<p onClick={this.someFunc}>test</p>
Because you need to pass some arguments the first one which I mentioned will be your desired one.
You can read their document for further details: React Handling
Events
The reason is because you write
<p onClick={this.someFunc('hello')}>test</p>
instead of
<p onClick={this.someFunc}>test</p>.
If you put there the () the function will be called right on rendering, else only if clicked.
Try this arrow function
<p onClick={() => this.someFunc('hello')}>test</p>
Not tested, but should work correctly this way.
tl;dr: You are invoking this.someFunc('hello') when you render your class, not when you call your onClick property. To fix it, you should use an arrow function, as so:
<p onClick={() => this.someFunc('hello')}>test</p>
Now if you want to know why this happens, let me clarify what you are doing.
import React, { Component } from "react";
class Some extends Component {
constructor(props) {
super(props);
}
// You define a class method that when called will in turn call the
// console.log method, passing message as your argument.
// message will equal 'hello' in your example.
someFunc = (message) => {
console.log(message)
}
// You define a render method of your class, react will
// automatically call this method on render.
render() {
// Your render method (which again is called automatically) will return
// <p onClick={this.someFunc('hello')}>test</p>
// let's see what operations you are performing in this JSX return.
return (
// You first define a paragraph html element to be rendered.
// You then give your element an attribute called onClick.
// But oh no! You are assigning onClick with the value of:
// this.someFunc('hello')
//
// That is not good, as what you are in effect saying is:
// please call the method this.someFunc and pass a single argument: 'hello'
// then assign the return of that method call to my property named onClick.
// Therefore, every time you render this class, you are asking
// javascript to call this function and get its value (which is undefined).
// So while you think onClick is a function, it is not! it is only a value.
// A value which you are asking JavaScript to get for you on each render.
// This is because in JS, when you write functionName(),
// you are literally calling the function at that moment.
// See https://www.w3schools.com/js/js_function_invocation.asp
<p onClick={this.someFunc('hello')}>test</p>
// What you want to do instead is assign your onClick property
// a function! Not a value!
// You want to write, "When I click here, do: this.someFunc('hello')"
// to do that, you have some options, but the end goal will be the same
// What you need to do is assign onClick a value, which when called (onClick)
// will trigger your function! For instance:
// onClick={() => this.someFunc('hello')}
// Above we say "on each render, assign the onClick property a value which IS a function!"
// This is, an unnamed arrow function!
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
// Notice how we have defined an unnamed function that wraps our
// this.someFunc('hello') and we have NOT
// immediately called it (no parentheses following the wrapper function)
// # outer function \() => #inner function \functionName()\ \ <-- No trailing parentheses.
// Therefore, now the only way to execute your method named
// this.someFunc('hello') is by calling its wrapper function.
// And the only way to call that unnamed wrapper function?
// By invoking your onClick property of your p element.
)
}
}

Executing parent function from child component does not give proper results : React+Typescript

I am trying to execute the parent function by calling it from child component and it does not yield proper results. There is a Pagination Component that computes the offsets for the pagination and sends the data to the child by calling parents method.
Console log statement inside Parent gives wrong result where as inside child's setOffSet() gives proper result and when sent to parent component's method in the next line, It gives wrong result.
Code Sandbox: https://codesandbox.io/s/react-typescript-v91un
setState needs a callback function but you're immediately calling the handlePagination function so you'd be passing in the return value of handlePagination. Try this instead...
this.setState(
{ fromIndex },
() => this.props.handlePagination(this.state.fromIndex, NO_OF_RECORDS_PER_PAGE)
);

Using () => vs this.something in jsx

I am trying to comprehend when do we use something like in jsx of our react
something = {this._onRefresh}
something = {() => this._onRefresh}
something = {this._onRefresh()}
Where something could be something we are passing to our
child component
onChange Event
onClick event
Any other situation you can think about
Where our ._onRefresh() could be
._onRefresh = () => {
//changes state of our react component
//Call redux action
//call other functions
}
or In case of forms, it takes in event which triggered it
._onRefresh = (event) => {
//takes target value to search inside the data
//Store data in the backend
}
Can someone please explain me when do we need to use which one? will help me a lot in clearing my fundamentals of react and javascript
The points 1/2 and 3 are actually totally different.
Your point 3 executes a function and assigns the returning value to the something.
Your examples 1/2 assign a function to something
The case 3 could be used when for example you have the disable attribute, and you want to assign the returning true/false of a function.
The points 1 and 2 are assigning a function to the attribute, like for example with onClick attribute, which accepts a callback to be called on click.
The difference between the first and the second point is that if you put that code inside the render method, the second point creates a function for every render, which is not the best for performances.
Using the first point, you should pay attention on how you define the method for the reference of this inside the method.
If you define the class method as:
myMethod() {
console.log(this); // it will be undefined by default
}
Then you need to bind the this inside the constructor if you want to access this inside the method:
this.myMethod = this.myMethod.bind(this);
If you define the method as arrow function, it will keep the value of the this inside the method, so no need of the bind:
myMethod = () => {
console.log(this);
};
1- you are passing a function as a property to this component
2- you are creating a new function and passing it as a property to this component
3- you are passing the result (output) of calling _onRefresh as a property to this component
Option 1 is valid if _onRefresh is actual callback function that should be passed via a prop:
_onRefresh = () => console.log('refresh');
...
<Component onRefresh={this._onRefresh}/>
Where Component uses onRefresh like:
// do refresh
props.onRefresh();
Option 2 is valid if _onRefresh is actual callback function, and onRefresh prop is expected to be higher-order function by a component that accepts a callback:
_onRefresh = () => () => console.log('refresh');
...
<Component onRefresh={() => this._onRefresh}/>
Where Component handles onRefresh like:
// do refresh
const refreshFn = props.onRefresh();
refreshFn();
Option 3 is valid if _onRefresh is higher-order function that returns another function, and this is expected by a component that accepts a callback:
_onRefresh = () => () => console.log('refresh');
...
<Component onRefresh={this._onRefresh()}/>
Where Component handles onRefresh like:
// do refresh
const refreshFn = props.onRefresh();
refreshFn();
Scenarios in options 2 and 3 are much less probable because they don't have much uses in this particular case in React.
In this case option 1 is likely correct because _onRefresh does not return another function and is not expected by child component. Option 2 is a mistake that will result in _onRefresh being never called. Option 3 is a mistake that will result in _onRefresh being called instantly and undefined is not a function error later or, even worse, erroneous behaviour with no error.

What is the point of wrapping a function of react component if i want to pass it down as a property?

What is the point of this?
In the next example i found in book code we have a funtion in the component that changes component state createTimer()
createTimer = (timer) =>
{
const t = helpers.newTimer(timer);
this.setState({
timers: this.state.timers.concat(t),
});
client.createTimer(t);
};
It is wrapped:
handleCreateFormSubmit = (timer) => {
this.createTimer(timer); };
And passed down as property:
<ToggleableTimerForm
onFormSubmit={this.handleCreateFormSubmit}
/>
If you just do this:
<ToggleableTimerForm onFormSubmit={this.createTimer}/>
...and createTimer is a regular method of your class:
class YourComponent extends Component {
createTimer(timer) {
const t = helpers.newTimer(timer);
this.setState({
timers: this.state.timers.concat(t),
});
client.createTimer(t);
}
}
...then the issue would be that when the child component calls onFormSubmit, this wouldn't be set correctly.
But since you're setting a property of your instance and are using an arrow function:
class YourComponent extends Component {
createTimer = (timer) => {
const t = helpers.newTimer(timer);
this.setState({
timers: this.state.timers.concat(t),
});
client.createTimer(t);
};
}
...you don't have to worry about this being bound correctly, so you're right that you don't need the wrapper to fix that. Perhaps the wrapping function is there as a precautionary measure since the class method pattern is more common.
The only benefit you'd gain is if the child calls this.props.onFormSubmit with additional arguments that you want to ignore. If that's not the case, then you can leave out the wrapping function.
Generally you pass a function down that's bound to it's original component. This allows child components to alter the state of their parent. Imagine this Scenario :
I have a parent component with state property A. I have a function that takes an input and updates the state of the PARENT!!!!
I pass that as a prop to a child (maybe a form). When I submit the form, I call the function passed as a prop to update the PARENTS state with my form values.
A few things to keep in mind, lexical arrow functions LACK SCOPE, and if the function leverages the state of the component it must be bound to the component.
One problem I see in your code....
handleCreateFormSubmit requires a parameter. onFormSubmit will pas it one, but I don't think it'll be the one you're expecting. It'll pass the event. You can do something along these lines "
onFormSubmit={() => this.handleCreateFormSubmit(someValue)}

Set State not getting called in parent function in React

I have a function defined in my component class which is calling it's parent function where that child is being utilized.
completeSession(id) {
this.props.completeSession(id);
}
This function calls the parent function with id. The parent function is:
completeSession(id) {
console.log("Entered parent complete Session");
console.log(id);
this.setState({feedback_id: id});
console.log(this.state.feedback_id);
}
Here as you can see I have 3 console logs and my console.log(id) is giving a proper value but this.state.feedback_id comes as undefined. Over here the this.setState should update feedback_id and this should update the child component also.
renderPast() {
return(
<PastSessions timezone={this.state.timezone}
feedback_id={this.state.feedback_id}
sessions={this.state.sessions}
completeSession={this.completeSession.bind(this)}
cancelSession={this.cancelSession.bind(this)} />
)
}
So I'm passing the feedback id back to the component so I'm not sure why it is not setting the state as intended. Any help would be appreciated.
Please keep in mind that setState is asynchronous - so state is not updated immidiately after calling it. You can provide a callback function as setState second argument which will be called when the state is actually updated:
this.setState({feedback_id: id}, () => {
console.log('state updated');
console.log(this.state.feedback_id);
});
According to React setState documentation:
... the second parameter is an optional callback function that will be
executed once setState is completed and the component is re-rendered.
Generally we recommend using componentDidUpdate() for such logic
instead.
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value.

Categories

Resources