create-react-app-typescript
create-react-app-typescript copied to clipboard
Lambdas are forbidden in JSX attributes due to their rendering performance impact
Hi,
What i'm doing wrong ?
class ProtectedRoute extends React.Component<any, any> {
constructor(props: any) {
super(props);
}
public render() {
const { component: Component, ...rest } = this.props;
return (
<Route
{...rest}
render={ props => this.props.loggedIn ? (<Component {...props} />) : (<Redirect to={{ pathname: '/login', state: { from: this.props.location } }} />) }
/>
)
}
}
function mapStateToProps(state: any) {
return {
loggedIn: state.loggedIn
}
}
export default connect(mapStateToProps)(ProtectedRoute);
Exactly what that message indicates - you're using a lambda inside a JSX attribute in render. The jsx-no-lambda rule of tslint-react is enabled by default and reports such violations.
There are quite a lot of articles describing why using lambda in this position is problematic (at least may become so)... I'll leave that up to you to read one of them.
Thanks for response but is it really problematic to use lambas in that position ?
You should really read at least one of the quite huge amount of articles around that topic ...
Just to point you in a direction - the documentation of this rule mentions:
jsx-no-lambda Creating new anonymous functions (with either the function syntax or ES2015 arrow syntax) inside the render call stack works against pure component rendering. When doing an equality check between two lambdas, React will always consider them unequal values and force the component to re-render more often than necessary.
Using Lambda inside a render method is not as horrible as stated in this thread. In fact, it is quite convenient in many situations.
You can read further information on a Github thread from a few days ago. Dan Abramov explains briefly that it is okay. You just need to be careful not to have the lambda accessing value that change because it invalidates the shallow comparison. You can read more here: https://github.com/facebook/create-react-app/issues/4801#issuecomment-409553780
not to have the lambda accessing value that change
That's not what I'm taking away from that comment (maybe I'm misinterpreting). I take it to mean that if you pass in a lambda as a prop to a PureComponent, then shouldComponentUpdate will always return true, thus losing any benefit of PureComponent (and actually paying the albeit cheap cost of the shallow equals check).
read https://maarten.mulders.tk/blog/2017/07/no-bind-or-arrow-in-jsx-props-why-how.html
It's probably a stupid question but, in a case of a Button component that get props like disabled and an onClick function, I would usually do this :
<button disabled={disabled} onClick={() => !disabled && onClick}>
{children}
</button>
Now with the lint warning I have to do like so :
const handleOnClick = (event: React.MouseEvent<HTMLElement>) => {
if (!disabled) {
onClick(event)
}
}
return (
<button disabled={disabled} onClick={handleOnClick}>
{children}
</button>
)
Although I don't mind the code to be a bit more verbose, but now I am stuck if I need to pass down arg to that onClick callback.
Is there a way around that ? I am use to do that with arrow function in my onClick function previously but now I am not sure how to achieve this.
@marcantoinepelletier here's one way to do it:
public handleOnClick(event: React.SyntheticEvent<HTMLButtonElement>) {
// arg is available
console.log(event.currentTarget.dataset.arg); // outputs value of someArg
}
return (
<button data-arg={someArg} onClick={this.handleOnClick}>
{children}
</button>
)
@brammitch This is a specific question, but how would I do this in case of using a library like React Navigation?
static navigationOptions = ({ navigation }: NavigationScreenProps) => ({
headerLeft: Platform.select({
android: (
<Icon
name="md-menu"
type="ionicon"
containerStyle={styles.icon}
onPress={navigation.toggleDrawer} // Here?
/>
),
ios: null
}),
headerTitle: strings.homeTitle
});
Normally I would do onPress={() => navigation.toggleDrawer()}
And if my code is correct like I described it above, how would I avoid using a lambda function If I have to call it with an argument, let's say: onPress={() => navigation.navigate("HomeScreen")?
@janhesters Your example looks correct; if you're not passing any arguments, onPress={() => navigation.toggleDrawer()} can be simplified to onPress={navigation.toggleDrawer}.
If you need to pass an argument, React supports data attributes as I showed in my example.
<Icon
data-route="HomeScreen" // accessible from dataset.route
onPress={navigation.navigate}
/>
There's a great discussion of this rule at palantir/tslint-react#96.
See MDN - Using data attributes for more info. React components support data and other DOM Elements.
ok I get the point of no lamdas in JSX, but how do you implement a render prop component without them? if there is a way I'd like to know! thanks 😄
Follow up, I did some digging on this, the work arounds for not using lambdas in your render are a bigger performance hit then just turning off this lint setting and just using them.
especially for something like render props, but with react hooks this wont be an issue in the future 🎣
as always the profiler is your friend!
@GeorgiosP How does React Hook solve the issue of passing arguments without lamdas ?
I could type it all out but someone did the hard work for me 👍
Article on the different shared functionality implementations, hooks don't solve the issue of no lambdas in render it simply makes it a non issue since you wont need to write them 😄