Why are stateless functions not supposed to have methods? - javascript

I have read in multiple places that stateless functions in React are not supposed to have inner functions. Why is it so, though it works?
const Foo = () => {
let bar = () => {
return <span>lorem ipsum</span>
}
return <div>{bar()}</div>
}
This works. But, why is this not supposed to be done?

N.B. This answer assumes that the use of the word "method" was incorrect, and that we are actually talking about an inner function, as in the example provided in the question.
A stateless component is defined as a function which returns something that can be rendered by React:
const MyStatelessComponent = function (props) {
// do whatever you want here
return something; // something must be render-able by React
}
To (re-)render the component, React calls the function, so it makes sense to perform expensive computations in advance and save their result outside of the function.
In your toy example, the function bar is declared once per render, and only used once. Let's assume that it was slightly more complicated and pass it a single parameter:
const Foo = () => {
let bar = text => {
return <span>{text}</span>
}
return <div>{bar("lorem ipsum")}</div>
}
By moving bar outside of the component, you don't need to create the function once per render, you just call the function that already exists:
const bar = text => {
return <span>{text}</span>
}
const Foo = () => {
return <div>{bar("lorem ipsum")}</div>
}
Now your component is ever-so-slightly more efficient, since is does less work every time it is called.
Also note that bar is almost the same as a stateless component now, and could easily be turned into one by making the function take a props object rather than a single string argument.
But the bottom line is that you can do whatever you want inside the stateless component. It just is worth bearing in mind that it will happen once per (re-)render.

While still valid code, as far as react is concerned, the above example is not a stateless component.
A stateless component is basically a shortcut to the render method of a stateful component (without the same life-cycle) and should "ideally" only return data, not define methods or actually manipulate or create additional data or functionality. With a stateful component, ideally, you do not define methods within the render method so none should be added in a stateless component.
By defining a method, function, or parameter inside of a stateless component but outside of the render method, you are essentially saying that there is a possibility of manipulation within the stateless component, which defeats the purpose.
Mind you, it's still valid code...but just not "react" ideal.

The function Foo is basically the render method of the React component. Therefore, it will be called everytime the component needs to be rendered. By declaring a local function inside it, it will create a new function everytime the component re-renders, which is bad.
Either declare the function outside or implement a stateful component instead.

Related

How to pass the set[State] function to a non-descendent trigger component

Here is the diagram. ChildComponentB has a state - stateX. In ChildComponentA, once the event occurs, it will change the stateX in ChildComponentB.
If the ChildComponentA is the child component of ChildComponentB, then it's easy, just pass the setStateX as a prop to ChildComponentA. But in this case, it's not.
The real scenario is the following. I have a canvas component, there are some static Rectangles already there, once there are mouse move over the line of the Rectangles, I'd like to add the indicator lines to another child component of the canvas component.
Hence, the rectComponent is not the descendent of the distanceIndicatorsComponent. So I can't pass the setLines to RectComponent.
What's your approach to do that?
If I use useContext approach, will it work?
Thank you, #KonradLinkowski to provide your solution. Here is his code. However, useContext is still lifing the state up to ParentComponent.
import React, { useContext, createContext, useState } from "react";
const Context = createContext();
function ChildComponentA(props) {
const { setStateX } = useContext(Context);
return (
<div>
componentA button:{" "}
<button onClick={() => setStateX((i) => i + 1)}>add</button>
</div>
);
}
function ChildComponentB(props) {
const { stateX } = useContext(Context);
return <div> stateX is {stateX} </div>;
}
export default function ParentComponent(props) {
const [stateX, setStateX] = useState(0);
return (
<>
<Context.Provider value={{ stateX, setStateX }}>
<ChildComponentA> </ChildComponentA>
<ChildComponentB> </ChildComponentB>
</Context.Provider>
</>
);
}
Regarding the reusbility of the ComponentB i.e. distanceIndicatorsComponent in this scenario, it includes the JSX and the states plus the interface in which there are logic to change the states. The are all parts which should be reusable in the furture.
From OOP perspective, the lines (state) belongs to DistanceIndicatorsComponent, and the how to change the lines (Add Line in this case) should be also reusable logic which belongs to distanceIndicatorsComponent.
However, from React perspective, to lift the setLines (this is the interface triggered under some event) is not "good enough" from OOP perspective. To lift the state - lines and state management function - setLines up to CanvasComponent is a "not good enough" in terms of the encapsulation. Put a wrap component on top of ComponentB is the same thing, the setLines still can't be passed to FrameComponent unless FrameComponent is a child-component of the wrap component.
It's very common to see there is a very heavy component holding all the state and the events at the top. It makes me feel that's a bad smell of the code. The reusability of the component should be based on a set of components, in this set of components, there is one uncontrolled component at the top, and underneath of this uncontrolled component are controlled components. This set of components is a external reusability unit.
Here, in this diagram, there should be more than one reusable unit rather than one. If lift the state up to CanvasComponent, it makes all the components underneath are un-reusable. In some extents, you still can re-use the JSX of this component, but I'd say, in terms of reusablity, it should invovle as many reusable logic as possible.
I might be wrong, please correct me. And thank you for sharing your valuable comments.
Requirements
First let us sum up the requirements.
Rect Component and Distance Indicators have not much to do with each other. Making them aware of each other or creating a dependency between them would be not desired in a good OOP design.
The interaction between both is very specific. Establishing a mechanism or a data structure just for this special sort of interaction would add an overhead to all components that don't need this sort of interaction.
General Concepts
So you must use a mechanism that is so generic that it does not add any sort of coupling. You need to establish something between these two components, which only these two components know and which for all the rest of your program is nonsense. What mechanisms serve for such a purpose?
Function pointers
Lambda functions
Events
Function pointers and lambda functions are complicated constructs. Not everybody prefers to use them. Now you see why events are so popular. They address a common requirement of connecting two components without revealing any of the details of them to anybody.
I personally recommend you to use lambda functions in this situation. Because this is one strength of JavaScript. Search in google for callback or asynchronous lambda function. This often adds the least overhead to existing code. Because a lambda functions has an important property:
With lambda functions you can do things very locally. Doing things locally is an important design principle. You don't need to define extra methods or functions or classes. You can just create them wherever you are, return them, pass them freely around to where you actually need them and store them there. You can store them even without knowing what is behind them.
I think, this is your answer. The only thing you need is a mechanism to pass lambda functions and to store your lambda functions. But this is on a very generic level and therefore adds no coupling.
With events you are on similar path. The event mechanism is already there. But therefore you already have a good answer.
Example with pure JavaScript
When applying this to JavaScript we can imagine that function pointers could be compared to function expressions in JavaScript. And lambda functions can be compared to arrow functions in JavaScript. (Note: Arrow functions also provide "closures", which is required in this case, see How do JavaScript closures work?).
A simple example illustrates this:
class DistanceIndicator {
constructor(height, width) {
this.height = height;
this.width = width;
}
resize(height){
this.height = height;
}
incorrect_resizer(height){
return this.resize;
}
resizer(){
return (height) => this.resize(height);
}
resizer_with_less_overhead(){
return (height) => this.height = height;
}
}
p = new DistanceIndicator();
p.resize(19);
// If you want to use this, you have to store p. You may see
// this as not so nice, because, you are not interested in what
// actually p is. And you don't want to expose the information
// that it has a method resize. You want to have the freedom
// of changing such details without the need of changing all
// the code where something happens with Rectangles.
console.log(p.height);
resizer = p.incorrect_resizer()
//resizer(18);
// In this example, resizer is a function pointer. It would be
// nice to store it and be able to call it whenever we want to
// inform Rectangle about something interesting. But it does not
// work because the resize method cannot be isolated from the
// class. The "this" is not there.
console.log(p.height);
resizer = p.resizer();
resizer(17);
// That works. Lambda functions do the job. They are able to
// include the "this" object.
console.log(p.height);
resizer = p.resizer_with_less_overhead();
resizer(16);
console.log(p.height);
// As you have now a resizer, you can store it wherever you want.
// You can call it without knowing what is behind it.
The idea in the example is that you can store the resizers wherever you want without knowing what they are. You shouldn't name them resizer, but give them a generic name like size_notification.
Example for React
The React concept for contexts is a typical candidate for data exchange between components. But the principle of React is a pure unidirectional data flow (top-down). This is also true for the context, which means, we cannot use a context for what we want.
React does not provide support for the implementation of the proposed idea. React is only responsible for the pure construction of the HTML page and a comfortable and performant rendering. It is not responsible for the "business" logic of our HTML page. This is done in full JavaScript. That makes sense because you want be able to develop complex web applications. Therefore you need all your favourite programming concepts. A real application does not follow the design principle of React. React is only a presentation layer. Most people like OOP progamming.
So when implementing something with React we must keep in mind that React is just a library for JavaScript. The full power of JavaScript is always available and should be used for our web application.
After realizing this, the problem becomes simple. See this code:
import React from 'react';
let sizeNotificator = (newValue) => {console.log(newValue)};
function Rect(props) {
return <button onClick={() => sizeNotificator("12")}>resize to 12</button>;
}
class DistanceIndicator extends React.Component {
state = {
size: "0",
};
setSize(newValue) {
this.setState({
size : newValue
});
};
componentDidMount(){
sizeNotificator = ((newValue) => {this.setSize(newValue);})
}
render() {
return <p>Current size: { this.state.size}</p>;
}
}
class App extends React.Component {
render() {
return(<div>
<DistanceIndicator/>
<Rect/>
</div>);
}
}
export default App;
With this code the requirement is fulfilled that none of the DistanceIndicator implementation details are revealed to the outside of DistanceIndicator.
Obviously this example code only works if there is not more than one DistanceIndicator. To solve this is a different topic with probably not only one good solution.
If keeping the shared state in the ParentComponent is the problem, you can extract the Context.Provider to a separate component and pass components as it's children, those children can access the context value via useContext hook.
function ParentContextProvider({ children }) {
const [stateX, setStateX] = useState(0);
return (
<Context.Provider value={{ stateX, setStateX }}>
{children}
</Context.Provider>
);
}
export default function ParentComponent(props) {
return (
<ParentContextProvider>
<ChildComponentA />
<ChildComponentB />
</ParentContextProvider>
);
}
Now you can add any new state/setState to the ParentContextProvider and can pass it to it's children
Have you looked at Redux stores? You could have a variable like "showLine" or "originX"/"originY", then have one child dispatch changes, and the other child useSelector for the values?
Do you know if Redux works for your use case?
I prefer to use a simple events pattern for this type of scenario. Eg using a component such as js-event-bus.
CHILD COMPONENT A
props.eventBus.emit('MouseOverRectangle', null, new MyEvent(23));
CHILD COMPONENT B
useEffect(() => {
startup();
return () => cleanup();
}, []);
function startup() {
props.eventBus.on('MouseOverRectangle', handleEvent);
}
function cleanup() {
props.eventBus.detach('MouseOverRectangle', handleEvent);
}
function handleEvent(e: MyEvent) {
// Update state of component B here
}
RESULTS
This tends to result in quite clean encapsulation and also simple code. Eg any React conponent can communicate with any other, without needing to reveal internal details.

Converting React ES6 class to functional component - what to use for private methods?

I'm converting a React ES6 class-style component to a functional component. The one thing I'm slightly unsure of is how best to convert private class methods. As far as I can tell, I should convert them to functions within the functional component's function, as they need to be there to access the component's state. However, that presumably means that on each re-render, the function is going to get recreated:
Before
class Game extends React.Component {
handleClick(i) { if (this.state.foo) { ... } }
}
After
function Game {
function handleClick(i) { if (foo) { ... } }
}
Is this a problem performance-wise, and if so, is there any way to improve it?
Also, most guides I've read recommend assigning arrow functions to variables instead of just declaring functions, eg.
function Game {
const handleClick = (i) => { if (foo) { ... } }
}
Is this purely stylistic, or is there another reason to use assigned arrow functions over regular nested function definitions?
You can use functions defined with either the function keyword or the arrow function syntax. It does not really make a difference in this case. However, with the arrow syntax, functions do not get hoisted, and that may cause the linter to report a warning if you use a function before it is defined.
However, that presumably means that on each re-render, the function is
going to get recreated
You are correct, if you define functions either way, they will get recreated on every re-render. Whether that's a problem or not will depend on your use case.
If you use such a function inside a useEffect callback and add it to its dependency array, the effect will re-run on every re-render (which may not be what you want). If you pass such a function as a prop to any child component(s), that component(s) will also re-render.
You can wrap the functions in question with useCallback, and any child components that receive these as props with React.memo. However, you are now trading the cost of re-rendering components for the cost of storing and comparing props (React will be doing this, not you).
So really, this depends on your app. If the component in question has a large component tree below it, going with useCallback and React.memo might be worth it.

Problem Causing Variables to be Re-evaluated on React Re-renders

I'm finding a small issue causing let instantiated variables to be modified incorrectly within React functional components.
Expected Behavior
I have a variable (an object) that is modified by follow-on logic like this inside a React functional component:
const SomeComponent = () => {
let foo = {}
if (someCondition) {
delete foo[prop]
}
if (someOtherCondition) {
delete foo[anotherProp]
}
...
return (<> Object.keys(foo).map(key => {...})</>)
}
So basically, I'm trying to use only specific properties on this object based on other conditions, like a specific conditional use of the Component.
The foo variable is an object that's returned from a query to a database. Specifically, in the parent Component to SomeComponent, I'm running a query that returns information (in the form of an object) about a company. foo is one property from that return that is also an object. When the query is complete, the result of the query is passed down into SomeComponent, so foo is not stateful in SomeComponent.
The Problem
I'm noticing that, when the above Component is brought into the DOM the first time, it works as expected. So, for example, if I bring the Component into the DOM under "circumstance 1", it will work properly. However, if I then bring it into the DOM under another, different circumstance (let's call it "circumstance 2"--a circumstance when the foo object should have a different set of props deleted from it that when rendered under "circumstance 1", foo will not be modified correctly.
Specifically, I notice that foo will have both the properties deleted from it as if it met both someCondition and someOtherCondition.
If you have any leads on this challenge, I'd appreciate it!

Function inside functional component in React hooks - Performance

Need suggestion on having function within a functional component in react Hooks.
As far as I researched, many are saying it is bad practice
because it creates nested/inner function every time we call re-render.
After doing some analysis,
I found we can use onClick={handleClick.bind(null, props)} on the element and place the function outside the functional component.
Example:
const HelloWorld = () => {
function handleClick = (event) => {
console.log(event.target.value);
}
return() {
<>
<input type="text" onChange={handleClick}/>
</>
}
}
Please advise if there is any alternative way.
Thanks in advance.
Don't worry about it
Don't worry about creating new functions on each render. Only in edge cases does that impede your performance.
Setting onClick handlers are not one of those, so just create a new function on each render.
However, when you need to make sure you use the same function every time, you can use useCallback
Why not use useCallback for onClick
Here is a reason why you shouldn't bother with useCallback for onClick handlers (and most other event handlers).
Consider the following code snippets, one without useCallback:
function Comp(props) {
return <button onClick={() => console.log("clicked", props.foo)}>Text</Button>
}
and one with useCallback:
function Comp(props) {
const onClick = useCallback(() => {
console.log("clicked", props.foo)
}, [props.foo])
return <button onClick={onClick}>Text</Button>
}
The only difference in the latter is that React doen's have
to change the onClick on your button if props.foo remains the same.
Changing the callback is a very cheap operation, and it's not at all
worth complicating your code for the theoretical performance improvement it gives.
Also, it's worth noting that a new function is still created on every render
even when you use useCallback, but useCallback will return the old one
as long as the dependencies passed as the second argument are unchanged.
Why ever use useCallback
The point of using useCallback is that if you compare two functions with reference
equality, fn === fn2 is true only if fn and fn2 point to the same function in memory.
It doesn't matter if the functions do the same.
Thus, if you have memoisation or otherwise only run code when the function changes,
it can be useful to use useCallback to use the same function again.
As an example, React hooks compare old and new dependencies, probably using Object.is.
Another example is React.PureComponent, which will only re-render when props or state have changed. This can be useful for components that use a lot of resources to render. Passing e.g. a new onClick to a PureComponent on each render will cause it to re-render every time.
many are saying it is bad practice because it creates nested/inner function every time we call re-render
No, inner functions / closures are so common, there is no problem with them. The engine can heavily optimize those.
The point here is that you pass the function as a prop to the child component. And as the function was "recreated", it does not equal the previous function passed, annd thus the child does rerender (and that's whats bad for performance).
You can resolve that with useCallback, which memoizes the function reference.
Interesting question, me and my coleagues had some worries about this, so I did a test.
I have created 1 Component with Hooks and 1 Component with Class, put some functions there and then render it 1000x times.
The Component with Class looks like this:
export class ComponentClass extends React.PureComponent {
click1 = () => {
return console.log("just a log");
};
render() {
return (
<>
<span onClick={this.click1}>1</span>
</>
);
}
}
The Component with Hooks looks like this:
export const ComponentHook = React.memo((props) => {
const click1 = () => {
return console.log("just a log");
};
return (
<>
<span onClick={click1}>1</span>
</>
);
});
I have added more click handlers to the components and then rendered them some 1000s times, the Class is faster as it does not define the functions each render, if you increase the number of functions defined, then the difference will be bigger:
Here it is a codesandbox so you can test the performance Class vs Hooks : https://codesandbox.io/s/hooks-vs-class-forked-erdpb
useCallback
You can use useCallback feature :
const HelloWorld = ({ dispatch }) => {
const handleClick = useCallback((event) => {
dispatch(() => {console.log(event.target.value)});
})
return() {
<>
<input type="name" onChange={handleClick}/>
</>
}
}
useCallback will return a memoized version of the callback that only
changes if one of the dependencies has changed. This is useful when
passing callbacks to optimized child components that rely on reference
equality to prevent unnecessary renders (e.g. shouldComponentUpdate).
For further details visit react documentation reference: React useCallback
Old Solution
First solution:
To pass the your handleClick function to your functional component.
const HelloWorld = (props) => {
return() {
<>
<input type="name" onChange={props.handleClick}/>
</>
}
}
Second solution:
To define your function outside of your functional component.
Inspired by #tibbus 's benchmark, I made this one that tests the perfomance using or not the useCallback hook. After several executions, it seams that the use of useCallback can be very important for high frequency rendering.
Execution 1
Execution 2
Execution 3
https://codesandbox.io/s/usecallback-vs-raw-definition-xke9v?file=/src/App.js
As per React Documentation (ending part),
The problem with latter syntax is that a different callback is created
each time the LoggingButton renders. In most cases, this is fine.
However, if this callback is passed as a prop to lower components,
those components might do an extra re-rendering. We generally
recommend binding in the constructor or using the class fields syntax,
to avoid this sort of performance problem.
Class field syntax:
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>
);
}
}
arrow function in the callback syntax:
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// This syntax ensures `this` is bound within handleClick
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}
}
I would honestly just use a class component in these cases. I'm aware of premature optimization, but creating a new function each time does just seem like extravagant wastefulness without much of a maintainability upside. tibbus has demonstrated the perf hit, and inline functions are arguably less readable than class methods. All you're losing out is the slick feeling of writing a functional component.
Just useCallback
Why would you need to define a function inside a component and not anywhere else? Because you either have to pass it to another child compononent, or you have to use it in an effect, memo, or another callback. For any of those cases if you dont wrap your function in useCallback you will be passing a new function and causing the component to rerender, the memo to re-run, the effect to re-run, or the callback to re-define.
You can never avoid the performance hit of redefining the function itself, but you can avoid the performance hit of performing any computation that has that function as a dependency to know if it has to run or not (be it a component or hook).
So... just wrap every function in your component in useCallback and forget about it, never seen a single in case in which this would cause any harm. If you can define the function outside the component, thats always better.

How unperformant are anonymous functions in React component attributes?

You're not supposed to use anonymous functions in react attributes, e.g.
<a onClick=()=>doIt('myId')>Aaron</a>
I understand why this creates a performance problem for React's reconciliation because that anonymous function is recreated on every render pass and will thus always trigger a real DOM re-render of some kind. My question is, for a small component (i.e. not table where every row has a link) is this insignificant? I mean, React is smart enough just to replace the handler, not to re-render the DOM, right? so the cost is not that high?
I feel obliged to inform you that using an Anonymous function and Function.bind(this) in the render triggers a new render. This is because both
doIt.bind(this, 'myId') === doIt.bind(this, 'myId') // false
AND
() => doIt('myId') === () => doIt('myId') // false
are FALSE!
If you want to bind something to a function, use partial application with a method in the React class.
class myComponent extends Component {
doIt = (id) => () => {
// Do Something
}
render() {
<div>
<a onClick={this.doIt('myId')}>Aaron</a>
</div>
}
}
For:
small components: you are ok (almost no performance issues)
large components: the deeper you get the more try to avoid it
In React documentation about event handling, you can find:
In most cases, this is fine. However, if this callback is passed as a prop to lower components, those components might do an extra re-rendering. We generally recommend binding in the constructor or using the class fields syntax, to avoid this sort of performance problem.
Note: React is not handling callback props differently than other props. It always compares the old and new prop. Thus it re-renders when anonymous function is present because the anonymous function is always newly created.
Your JSX code sample should actually look like this:
<a onClick={ ()=>doIt('myId') }>Aaron</a>
Using an anonymous fat arrow function like this is perfectly valid. You are supposed to use anonymous functions in react attributes. That's okay.
Yes, it's not a best practice. If you want to solve the this context issue when using the ES6 class extends React.Component syntax I would advise you to bind the function in the class constructor.
No, for a 'small' component (i.e. not table where every row has a link) this is not a significant performance issue. You are safe.

Categories

Resources