Strange behavior of parametrised function passed as a prop in React - reactjs

I am probably not seeing some obvious thing right here, but i feel pretty stuck with this one.
I have a function
public handleTest = (testNum: number) => {
console.log(testNum);
};
And the following case: I have a component to which I want to pass this function to further use it there onCLick event.
<Controls handleTest={() => this.handleTest}>
<Button label="Test1" clicked={() => this.handleTest(42)} />
</Controls>
The child component is the following:
interface IProps {
handleTest: (type: number) => void;
}
class Controls extends React.Component<IProps, {}> {
public render() {
const {
handleTest
} = this.props;
return (
<React.Fragment>
{this.props.children}
<button onClick={handleTest(42)} label="Test2" />
</React.Fragment>
);
}
}
The interesting thing about this case is that in case of Test2 button, it seems not to recognize the argument passed to it while it logs an object -
Object { dispatchConfig: {…}, _targetInst: {…}, nativeEvent: click, type: "click", target: button.sc-bwzfXH.gonlMM, currentTarget: button.sc-bwzfXH.gonlMM, eventPhase: 3, bubbles: true, cancelable: true, timeStamp: 689, … }
In case of Test1 everything works correctly. I am wondering what am I doing wrong and whether it is typescript that messes things up or some mistake of mine

This is because when you are initilizing the <Controls /> component, the function which you are sending is something like this:
() => this.handleTest
So in the button Test2 you are executing nothing because last function just return a function
So if you want to solve this:
<Controls handleTest={(number) => this.handleTest(number)}>
<Button label="Test1" clicked={() => this.handleTest(42)} />
</Controls>
Also if you do it in this way when <Controls /> is being rendered, your function is gonna be executed and not when the user click on it. To solve this you need to change it in this way:
<React.Fragment>
{this.props.children}
<button onClick={()=>handleTest(42)} label="Test2" />
</React.Fragment>
To optimize just send the function. This is function injection
<Controls handleTest={this.handleTest}>
<Button label="Test1" clicked={() => this.handleTest(42)} />
</Controls>

You're passing a function call to the handler, this would only work if you had curried the original function instead.
<button onClick={handleTest(42)} label="Test2" /> instead of
<button onClick={() => handleTest(42)} label="Test2" />
<button onClick={handleTest(42)} label="Test2" /> would work if handleTest looked like this:
public handleTest = (testNum: number) => () => {
console.log(testNum);
};
EDIT: I would actually recommend utilizing handlers the second way, when you need to infuse an outside parameter into the handler function. By declaring a handler through
{() => doSomething()}, every time you render the component that handler function will be initialized again.
Regardless, that's just a small optimization in most cases.

Related

"Expected 1 arguments, but got 0." for onClick event in React with Typescript

I have a handleClick function that I'm trying to pass as a prop for my onClick event within a component. This event just takes a setState function. I set an interface to type this as "handleClick: React.Dispatch<React.SetStateAction>;" as nothing else worked and would always give errors, so I assumed all was well, until I went ahead with writing the onClick event into the component declaration, when the error in the title appeared.
Here's the relevant code:
interface IProps {
handleClick: React.Dispatch<React.SetStateAction<boolean>>;
icon?: JSX.Element;
}
const NavLinks: React.FC<IProps> = ({ handleClick }) => (
<div className="sidebar_navlinks">
{sidebar_links.map((link) => (
<NavLink key={link.name} to={link.to} onClick={() => handleClick && handleClick()}>
<div className="link">
<link.icon className="icon" />
{link.name}
</div>
</NavLink>
))}
</div>
)
And then with that component I just do something like
<NavLinks handleClick={() => setMenuState(false)} />
How can I best type this so it stops giving the error in the title? I'm not clear why it would expect there's a value when I'm typed it to be something that sets state?
I see stuff online that, more often than not, is assuming the onClick is going to apply to an HTML button element, but I'm just using this to click on react-icons, so I'm even more lost.
handleClick should be of type () => void since setMenuState is wrapped in a function.
interface IProps {
handleClick: () => void;
icon?: JSX.Element;
}
If you passed setMenuState directly like:
<NavLinks handleClick={setMenuState} />
then it can be typed as a setState function

Handle children.props

so, in ParentComponent I have
<Component dropDownContent={<DropDownContent content={array} onSelect={handleSelect} />} />
my DropDownContent looks something like this
return (<ul>
{content.map((item) =>
{ return <li><button onClick={()=> onSelect(array.id)}>{array.name}</button></li>}
)}
</ul>)
Can I some how do something with the onSelect inside Component even if I add DropDownContent as a prop to Component?
Thanks :)
What I understand from your question is that you want to pass a function from the parent component to the child component. And when a local function inside child component is clicked, you want to call that passed function. if yes, then this is your solution:
Note: I do not know exactly what code you wrote and what your component consists of. So I will give you the answer by giving a simple example to fully understand the solution.
In your parent component:
export const ParentComponent = props => {
const handleSelect = () => console.log(`do something here`);
return (
<Component
dropDownContent={
<DropDownContent
content={array}
onSelect={() => handleSelect()}
/>}
/>
)
}
And in your child component you need to receive the passed function like below:
export const ChildComponent = props => {
const handlePassedFunction = () => props.onSelect?.();
return (<ul>
{content.map((item) => {
return <li>
<button onClick={() => handlePassedFunction(array.id)}>{array.name}</button>
</li>
}
)}
</ul>)
}

why <button onClick={this.props.onClick}> is different from <button onClick={(e) => {this.props.onClick(e)}}> here?

I found the following code in this post.(sandbox) I am not sure why these buttons behave differently.
One possible explanation is: Because Button's render method is not invoked for updating, the first button's click handler remains the same. However, this.props of the Button instance has changed to include the new onClick prop. In other words,if the props of a component element is changed, nextProps will finally become this.props even when shouldComponentUpdate returns false.
const submit = val => alert(val);
class App extends React.Component {
state = { val: "one" }
componentDidMount() {
this.setState({ val: "two" })
}
render() {
return <Form value={this.state.val} />
}
}
const Form = props => (
<Button
onClick={() => {
submit(props.value)
}}
/>
)
class Button extends React.Component {
shouldComponentUpdate() {
// lets pretend like we compared everything but functions
return false
}
handleClick = () => this.props.onClick()
render() {
return (
<div>
<button onClick={this.props.onClick}>This one is stale</button>
<button onClick={() => this.props.onClick()}>This one works</button>
<button onClick={this.handleClick}>This one works too</button>
</div>
)
}
}
Your explanation is correct on that when ShouldComponentUpdate() returns false, the Button component does not re-render on props change.
In the first <button> element, the onClick event is this.props.OnClick which is actually the function () => { submit("one"); } at the moment of the initial rendering. "one" is here because at this particular time point in the React lifecycle, Form's props.value evaluates to "one". Note that the function gets executed has nothing to do with Button's props.
In the second <button> element, however, the onClick event is () => this.props.onClick(). Regardless whether the Button component gets re-rendered, it is always the this.props.onClick() that get executed, where this.props changes when the parent components get state/props updates.
In the case of ShouldComponentUpdate() returning true, Button component will re-render on props updates. The first button's onClick event becomes a new function () => { submit("two"); } on the re-render triggered by the App component's state change. The two button elements' onClick event functions are never the same function, although they appear to produce the same result.
<button onClick={this.props.onClick}> Will call your onClick function and send an event object as its first parameter. This is the equivalent of doing this :
<button onClick={event => { this.props.onClick(event) }}>
<button onClick={() => this.props.onClick()}> Will set what your onClick function returns as the onClick function... It is the short syntax of the following code :
<button onClick={() => { return this.props.onClick() }}>
What you may be looking for is this : <button onClick={() => { this.props.onClick() }}>
In this case, clicking will simply call your function without sending any arguments
I suggest reading this documentation to know more about arrow/anonymous functions and their syntax

Lambda function inside render props

I am using render props:
<MyComponent>
{myProps => (
<button onClick={({ target: { value } }) => myProps.myFunction(value)} />
)}
</MyComponent>
However the onClick={({ target: { value } }) => myProps.myFunction(value)} is not optimized, as it creates a new function on each render.
My naive solution is:
const handleClick = props => ({ target: { value } }) => props.myFunction(value);
<MyComponent>
{myProps => (
<button onClick={handleClick(myProps)} />
)}
</MyComponent>
But if I understand well, unless handleClick is somehow memoized then it's quite useless.
Is there a nice way to optimize lambda functions in render props?
Note: Bonus point if it only uses stateless functional components, optionally with recompose.

Trigger onProperty of component

I have tried to trigger (refer, call, correct terminology?) an onScale from the property of this component from the react-native-photo-view API:
<PhotoView
source={{ uri: }}
onLoad={() => console.log('onLoad called')}
onTap={(event) =>
console.log(`onTap called: ${event.nativeEvent.x}${event.nativeEvent.y}`)}
onScale={() => console.log('onScale called')}
minimumZoomScale={1}
maximumZoomScale={3}
scale={0.5}
resizeMode={'contain'}
androidScaleType={'fitXY'}
style={styles.photo}
/>
How could i "trigger" this onScale={} property from lets say a <Button onClick={triggerOnScale()} /> to zoom with the scale factor? Any direct code example or pointers to techniques/methods that could help would be greatly appreciated!
If onScale is a method of your PhotoView component then you can set up a reference to call it:
handleOnClick = () => this.photoView.onScale()
//just use the handleOnClick wherever you want your click and it will call onScale also
<PhotoView
source={{ uri: }}
onLoad={() => console.log('onLoad called')}
onTap={(event) =>
console.log(`onTap called: ${event.nativeEvent.x}${event.nativeEvent.y}`)}
**ref={(photoView) => this.photoView = photoView}**
minimumZoomScale={1}
maximumZoomScale={3}
scale={0.5}
resizeMode={'contain'}
androidScaleType={'fitXY'}
style={styles.photo}
/>
If you want your onScale handler and your button's onClick handler to fire the same function, then you can just bind a function to the containing component and then pass the function's reference to the Button and PhotoView respectively.
class YourComponent extends Component {
constructor(props){
super(props);
this.someFunction = this.someFunction.bind(this);
}
someFunction() {
console.log('Inside some Function.);
}
render() {
return(
<div>
<PhotoView onScale={this.someFunction} />
<Button onClick={this.someFunction} />
</div>
);
}
}

Resources