For rendering smaller components/jsx within a bigger component, there are multiple approaches that one can follow. For example, consider this:
Method 1:
function BigComponent(props) {
const renderSmallComponent1 = () => <div>{props.a}</div>;
const renderSmallComponent2 = () => <div>{props.b}</div>;
return (
<div>
{renderSmallComponent1()}
{renderSmallComponent2()}
</div>
)
}
Method 2:
function BigComponent(props) {
const smallComponent1 = <div>{props.a}</div>;
const smallComponent2 = <div>{props.b}</div>;
return (
<div>
{smallComponent1}
{smallComponent2}
</div>
)
}
Method 3:
function SmallComponent1({ a }) {
return <div>{a}</div>;
}
function SmallComponent2({ b }) {
return <div>{b}</div>;
}
function BigComponent(props) {
return (
<div>
<SmallComponent1 a={props.a} />
<SmallComponent2 b={props.b} />
</div>
)
}
I am just trying to understand the difference in these 3 in terms of
dev experience,
how the framework treats them,
are there any performance optimizations,
are there differences in runtime behaviours in all of these?
Is either one better to use in certain scenarios?
These are the things that I understand:
in Method 3, all SmallComponent are React components which are rendered in another component, so they would have a component lifecycle, while in method 1 and 2, they are simple jsx, which does not have lifecycle, so they would not be mounted / unmounted as React components
in Method 2, we would be eagerly evaluating the JSX as it is directly a variable, while in method 1, it would only be evaluated when the function is called in render. So, in case, we have any conditional rendering, the eager evaluation might just be wasteful.
A few other helpful articles:
https://medium.com/missive-app/45-faster-react-functional-components-now-3509a668e69f
https://kentcdodds.com/blog/dont-call-a-react-function-component
UPDATE: it seems observation 1 is incorrect as all 3 of them would still be rendered as react components, and hence would have a component lifecycle. So react would mount/unmount them.
UPDATE 2: No, observation 1 is correct, method 1 and 2 are both treated as regular jsx as part of the BigComponent and they are not treated as react component which have a lifecycle.
UPDATE 3:
There is another method Method 4:
function BigComponent(props) {
const SmallComponent1 = () => {
return <div>{props.a}</div>;
}
const SmallComponent2 = () => {
return <div>{props.b}</div>;
}
return (
<div>
<SmallComponent1 />
<SmallComponent2 />
</div>
)
}
this is similar to Method 3, but Method 3 vs Method 4 is slightly different in execution, when debugging through dev tools.
Method 2:
function BigComponent(props) {
const smallComponent1 = <div>{props.a}</div>;
const smallComponent2 = <div>{props.b}</div>;
return (
<div>
{smallComponent1}
{smallComponent2}
</div>
)
}
If you want to a large UI into seperate smaller UI, this method will give you best performance because
It is still just one big UI component.
react just have to solve variable references.
while re-rendering, BigComponent,smallComponent1 and smallComponent2 are rendered together as single unit.
smallComponent1 and smallComponent2 cannot have their own state, life cycles and hooks.
smallComponent1 and 2 needs to be re-initialized everytime Bigcomponent state is changed. So it is good practise to wrap them with useMemo() if the result of those smallComponents are coming from an expensive computation.
Method 3:
function SmallComponent1({ a }) {
return <div>{a}</div>;
}
function SmallComponent2({ b }) {
return <div>{b}</div>;
}
function BigComponent(props) {
return (
<div>
<SmallComponent1 a={props.a} />
<SmallComponent2 b={props.b} />
</div>
)
}
React needs to resolve reference as well as execute the function after resolving the reference.
It is a composition of react's actual child components into a large Component.
Child components are allowed to have their own hooks.
Child components are not re-initialized but are re-rendered if BigComponent state is changed.
There is chance of SmallComponent1 and SmallComponent2 getting re-rendered multiple times on BigComponents rendering once if small components are updating thier own state based on props change in parents.
if each SmallComponents are supposed to use multiple props which state of BigComponents, Keeping SmallComponents outside BigComponent does offer good developer experience.
I hope Method 1 and Method 4 can also be understood using these above points.
Note: childcomponents stored in variable and childcompoents as function becomes tricker if your application logic is using ref or DOM element for maininting focus or anchor point of rendering.
Have you taken a look at the compiled JS in a React project?
JSX tags are essentially transformed in to React.createElement statements. You can read the docs here. Essentially the syntax is:
React.createElement(FunctionOrClassComponent, { props }, ...children)
In all three of your examples this would take place. In all three examples, the smaller components are functional components rather than class components. That is to say, they don't have the React lifecycle methods of a class component, but they can use equivalent React hooks - should you want to.
Edited: Evaluation (instantiation and rendering) depends on your render logic. If you have conditional rendering statements or your functions return null (or less content) based on certain conditions, then obviously you're doing less work. And as you rightly pointed out in the comments below, when you assign a JSX.Element to a variable, that is evaluated inline rather than as a result of a function - so that happens immediately.
To me, all three are valid approaches. To address your questions:
dev experience,
for small components with minimal state, functional components as variables or lambdas are convenient to write and easily read/parsed when revisiting code at a later date. When a component becomes more complex, you may have to reconsider how it's written and perhaps use Class components.
how the framework treats them,
to my knowledge the framework treats all three of your examples the same in terms of compilation. I'm unsure about rendering optimisation.
are there any performance optimizations,
your examples don't depict anything computationally onerous so performance optimization options are not so obvious
are there differences in runtime behaviours in all of these?
they are all translated to React elements, monitored for props changes, and re-rendered if parents re-render (if things like React.memo are not employed) -- there may be differences vs class-based elements, but I would guess that the runtime differences between your three examples are minimal
Is either one better to use in certain scenarios?
The differences between all three are more a matter of standards or etiquette than functional outcome. As a developer, I would be able to read and understand all three, but working in a team - I would want to see a standard approach.
Related
Imagine situation:
const [value, setValue] = useState(false);
const setSomething = (val) => {
setValue((prev) => {
fn(); dispatch(action); // or any other side effect
return prev + val;
});
};
Is it programmatically okey and fine with react principles to call side effects inside useState callback? May it affect the render process somehow?
It is not ok to use side effects inside the updater function. It might affect the render process, depending on the specific side effect.
It is not fine with react principles (separation of concerns, declarative code).
(I remember to have seen some exceptional use cases where putting some code inside the updater function was said to be the only solution, but I can't remember what it was. I'd appreciate an example in the comments.)
1. Consequences of using side effects
It is not ok to use side effects, basically for the same reasons why you shouldn't use side effects outside useEffect anywhere else.
Some side effects might affect the render process, other side effects might work fine (technically), but you are not supposed to rely on what happens inside the setter functions.
React guarantees that e.g. if you call setState( prev => prev + 1 ), then state would now be one more than before.
React does not guarantee what will happen behind the scenes to achieve that goal. React might call these setter functions multiple times, or not at all, and in any order:
StrictMode - Detecting unexpected side effects
... Because the above methods might be called more than once, it’s important that they do not contain side-effects. ...
2. following react principles
You should not put side effects inside the updater function, because it validates some principles, like separation of concerns and writing declarative code.
Separation of concerns:
setCount should do nothing but setting the count.
Writing declarative code:
Generally, you should write your code declarative, not imperative.
I.e. your code should "describe" what the state should be, instead of calling functions one after another.
I.e. you should write "B should be of value X, dependent on A" instead of "Change A, then change B"
In some cases React doesn't "know" anything about your side effects, so you need to take care about a consistent state yourself.
Sometimes you can not avoid writing some imperative code.
useEffect is there to help you with keeping the state consistent, by allowing you to e.g. relate some imperative code to some state, aka. "specifying dependencies".
If you don't use useEffect, you can still write working code, but you are just not using the tools react is providing for this purpose. You are not using React the way it is supposed to be used, and your code becomes less reliable.
Examples for problems with side effects
E.g. in this code you would expect that A and B are always identical, but it might give you unexpected results, like B being increased by 2 instead of 1 (e.g. when in DEV mode and strict mode):
export function DoSideEffect(){
const [ A, setA ] = useState(0);
const [ B, setB ] = useState(0);
return <div>
<button onClick={ () => {
setA( prevA => { // <-- setA might be called multiple times, with the same value for prevA
setB( prevB => prevB + 1 ); // <-- setB might be called multiple times, with a _different_ value for prevB
return prevA + 1;
} );
} }>set count</button>
{ A } / { B }
</div>;
}
E.g. this would not display the current value after the side effect, until the component is re-rendered for some other reason, like increasing the count:
export function DoSideEffect(){
const someValueRef = useRef(0);
const [ count, setCount ] = useState(0);
return <div>
<button onClick={ () => {
setCount( prevCount => {
someValueRef.current = someValueRef.current + 1; // <-- some side effect
return prevCount; // <-- value doesn't change, so react doesn't re-render
} );
} }>do side effect</button>
<button onClick={ () => {
setCount(prevCount => prevCount + 1 );
} }>set count</button>
<span>{ count } / {
someValueRef.current // <-- react doesn't necessarily display the current value
}</span>
</div>;
}
No, it is not ok to issue side-effects from a state updater function, it is to be considered a pure function.
The function return values are identical for identical arguments (no variation with local static variables, non-local variables, mutable reference arguments or input streams), and
The function application has no side effects (no mutation of local static variables, non-local variables, mutable reference arguments or input/output streams).
You may, or may not, be using the React.StrictMode component, but it's a method to help detect unexpected side effects.
Detecting unexpected side effects
Conceptually, React does work in two phases:
The render phase determines what changes need to be made to
e.g. the DOM. During this phase, React calls render and then
compares the result to the previous render.
The commit phase is
when React applies any changes. (In the case of React DOM, this is
when React inserts, updates, and removes DOM nodes.) React also calls
lifecycles like componentDidMount and componentDidUpdate during
this phase.
The commit phase is usually very fast, but rendering can be slow. For
this reason, the upcoming concurrent mode (which is not enabled by
default yet) breaks the rendering work into pieces, pausing and
resuming the work to avoid blocking the browser. This means that React
may invoke render phase lifecycles more than once before committing,
or it may invoke them without committing at all (because of an error
or a higher priority interruption).
Render phase lifecycles include the following class component methods:
constructor
componentWillMount (or UNSAFE_componentWillMount)
componentWillReceiveProps (or UNSAFE_componentWillReceiveProps)
componentWillUpdate (or UNSAFE_componentWillUpdate)
getDerivedStateFromProps
shouldComponentUpdate
render
setState updater functions (the first argument) <--
Because the above methods might be called more than once, it’s
important that they do not contain side-effects. Ignoring this rule
can lead to a variety of problems, including memory leaks and invalid
application state. Unfortunately, it can be difficult to detect these
problems as they can often be non-deterministic.
Strict mode can’t automatically detect side effects for you, but it
can help you spot them by making them a little more deterministic.
This is done by intentionally double-invoking the following functions:
Class component constructor, render, and shouldComponentUpdate methods
Class component static getDerivedStateFromProps method
Function component bodies
State updater functions (the first argument to setState) <--
Functions passed to useState, useMemo, or useReducer
Take a cue from the two highlighted bullet points regarding the intentional double-invoking of state updater functions and treat the state updater functions as pure functions.
For the code snippet you shared, I see no reason at all for the functions to be called from within the updater callback. They could/should be called outside the callback.
Example:
const setSomething = (val) => {
setValue((prev) => {
return prev + val;
});
fn();
dispatch(action);
};
I would not
Just because it works doesn't mean it's a good idea. The code sample you shared will function, but I wouldn't do it.
Putting unrelated logic together will confuse the next person who has to work with this code; very often, that "next person" is you: you, six months from now, after you've forgotten all about this code because you finished this feature and moved on. And now you come back and discover that some of the silverware has been stored in the bathroom medicine cabinet, and some of the linens are in the dishwasher, and all the plates are in a box labeled "DVDs".
I don't know how serious you are about the specific code sample you posted, but in case it's relevant: if you're using dispatch that means you've set up some kind of reducer, either with the useReducer hook, or possibly with Redux. If that's true, you should probably consider whether this boolean belongs in your Redux store, too:
const [ value, setValue ] = useState(false)
function setSomething(val) {
fn()
dispatch({ ...action, val })
}
(But it might not, and that's fine!)
If you're using actual Redux, you'll also have action-creators, and that's generally the correct place to put code that triggers side effects.
Regardless of whatever state tech you're using, I think you should prefer to avoid putting side-effect code into your individual components. The reason is that components are generally supposed to be reusable, but if you put a side-effect into the component that is not essential to display or interaction of the thing being visualized by the component, then you've just made it harder for other callers to use this component.
If the side-effect is essential to how this component works, then a better way to handle this would be to call setValue and the side-effect function directly instead of wrapping them up together. After all, you don't actually depend on the useState callback to accomplish your side-effect.
const [ value, setValue ] = useState(false)
function setSomething(val) {
setValue(value + val)
fn()
dispatch(action)
}
I understand that React tutorials and documentation warn in no uncertain terms that state should not be directly mutated and that everything should go through setState.
I would like to understand why, exactly, I can't just directly change state and then (in the same function) call this.setState({}) just to trigger the render.
E.g.: The below code seems to work just fine:
const React = require('react');
const App = React.createClass({
getInitialState: function() {
return {
some: {
rather: {
deeply: {
embedded: {
stuff: 1,
},
},
},
},
},
};
updateCounter: function () {
this.state.some.rather.deeply.embedded.stuff++;
this.setState({}); // just to trigger the render ...
},
render: function() {
return (
<div>
Counter value: {this.state.some.rather.deeply.embedded.stuff}
<br></br>
<button onClick={this.updateCounter}>Increment</button>
</div>
);
},
});
export default App;
I am all for following conventions but I would like to enhance my further understanding of how ReactJS actually works and what can go wrong or is it sub-optimal with the above code.
The notes under the this.setState documentation basically identify two gotchas:
That if you mutate state directly and then subsequently call this.setState this may replace (overwrite?) the mutation you made. I don't see how this can happen in the above code.
That setState may mutate this.state effectively in an asynchronous / deferred way and so when accessing this.state right after calling this.setState you are not guaranteed to access the final mutated state. I get that, by this is not an issue if this.setState is the last call of the update function.
This answer is to provide enough information to not change/mutate the state directly in React.
React follows Unidirectional Data Flow. Meaning, the data flow inside react should and will be expected to be in a circular path.
React's Data flow without flux
To make React work like this, developers made React similar to functional programming. The rule of thumb of functional programming is immutability. Let me explain it loud and clear.
How does the unidirectional flow works?
states are a data store which contains the data of a component.
The view of a component renders based on the state.
When the view needs to change something on the screen, that value should be supplied from the store.
To make this happen, React provides setState() function which takes in an object of new states and does a compare and merge(similar to object.assign()) over the previous state and adds the new state to the state data store.
Whenever the data in the state store changes, react will trigger an re-render with the new state which the view consumes and shows it on the screen.
This cycle will continue throughout the component's lifetime.
If you see the above steps, it clearly shows a lot of things are happening behind when you change the state. So, when you mutate the state directly and call setState() with an empty object. The previous state will be polluted with your mutation. Due to which, the shallow compare and merge of two states will be disturbed or won't happen, because you'll have only one state now. This will disrupt all the React's Lifecycle Methods.
As a result, your app will behave abnormal or even crash. Most of the times, it won't affect your app because all the apps which we use for testing this are pretty small.
And another downside of mutation of Objects and Arrays in JavaScript is, when you assign an object or an array, you're just making a reference of that object or that array. When you mutate them, all the reference to that object or that array will be affected. React handles this in a intelligent way in the background and simply give us an API to make it work.
Most common errors done when handling states in React
// original state
this.state = {
a: [1,2,3,4,5]
}
// changing the state in react
// need to add '6' in the array
// bad approach
const b = this.state.a.push(6)
this.setState({
a: b
})
In the above example, this.state.a.push(6) will mutate the state directly. Assigning it to another variable and calling setState is same as what's shown below. As we mutated the state anyway, there's no point assigning it to another variable and calling setState with that variable.
// same as
this.state.a.push(6)
this.setState({})
Many people do this. This is so wrong. This breaks the beauty of React and is bad programming practice.
So, what's the best way to handle states in React? Let me explain.
When you need to change 'something' in the existing state, first get a copy of that 'something' from the current state.
// original state
this.state = {
a: [1,2,3,4,5]
}
// changing the state in react
// need to add '6' in the array
// create a copy of this.state.a
// you can use ES6's destructuring or loadash's _.clone()
const currentStateCopy = [...this.state.a]
Now, mutating currentStateCopy won't mutate the original state. Do operations over currentStateCopy and set it as the new state using setState().
currentStateCopy.push(6)
this.setState({
a: currentStateCopy
})
This is beautiful, right?
By doing this, all the references of this.state.a won't get affected until we use setState. This gives you control over your code and this'll help you write elegant test and make you confident about the performance of the code in production.
To answer your question,
Why can't I directly modify a component's state?
Well, you can. But, you need to face the following consequences.
When you scale, you'll be writing unmanageable code.
You'll lose control of state across components.
Instead of using React, you'll be writing custom codes over React.
Immutability is not a necessity because JavaScript is single threaded, but it's a good to follow practices which will help you in the long run.
PS. I've written about 10000 lines of mutable React JS code. If it breaks now, I don't know where to look into because all the values are mutated somewhere. When I realized this, I started writing immutable code. Trust me! That's the best thing you can do it to a product or an app.
The React docs for setState have this to say:
NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
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.
There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.
setState() will always trigger a re-render unless conditional rendering logic is implemented in shouldComponentUpdate(). If mutable objects are being used and the logic cannot be implemented in shouldComponentUpdate(), calling setState() only when the new state differs from the previous state will avoid unnecessary re-renders.
Basically, if you modify this.state directly, you create a situation where those modifications might get overwritten.
Related to your extended questions 1) and 2), setState() is not immediate. It queues a state transition based on what it thinks is going on which may not include the direct changes to this.state. Since it's queued rather than applied immediately, it's entirely possible that something is modified in between such that your direct changes get overwritten.
If nothing else, you might be better off just considering that not directly modifying this.state can be seen as good practice. You may know personally that your code interacts with React in such a way that these over-writes or other issues can't happen but you're creating a situation where other developers or future updates can suddenly find themselves with weird or subtle issues.
the simplest answer to "
Why can't I directly modify a component's state:
is all about Updating phase.
when we update the state of a component all it's children are going to be rendered as well. or our entire component tree rendered.
but when i say our entire component tree is rendered that doesn’t mean that the entire DOM is updated.
when a component is rendered we basically get a react element, so that is updating our virtual dom.
React will then look at the virtual DOM, it also has a copy of the old virtual DOM, that is why we shouldn’t update the state directly, so we can have two different object references in memory, we have the old virtual DOM as well as the new virtual DOM.
then react will figure out what is changed and based on that it will update the real DOM accordingly .
hope it helps.
It surprises me that non of the current answers talk about pure/memo components (React.PureComponent or React.memo). These components only re-render when a change in one of the props is detected.
Say you mutate state directly and pass, not the value, but the over coupling object to the component below. This object still has the same reference as the previous object, meaning that pure/memo components won't re-render, even though you mutated one of the properties.
Since you don't always know what type of component you are working with when importing them from libraries, this is yet another reason to stick to the non-mutating rule.
Here is an example of this behaviour in action (using R.evolve to simplify creating a copy and updating nested content):
class App extends React.Component {
state = { some: { rather: { deeply: { nested: { stuff: 1 } } } } };
mutatingIncrement = () => {
this.state.some.rather.deeply.nested.stuff++;
this.setState({});
}
nonMutatingIncrement = () => {
this.setState(R.evolve(
{ some: { rather: { deeply: { nested: { stuff: n => n + 1 } } } } }
));
}
render() {
return (
<div>
Normal Component: <CounterDisplay {...this.state} />
<br />
Pure Component: <PureCounterDisplay {...this.state} />
<br />
<button onClick={this.mutatingIncrement}>mutating increment</button>
<button onClick={this.nonMutatingIncrement}>non-mutating increment</button>
</div>
);
}
}
const CounterDisplay = (props) => (
<React.Fragment>
Counter value: {props.some.rather.deeply.nested.stuff}
</React.Fragment>
);
const PureCounterDisplay = React.memo(CounterDisplay);
ReactDOM.render(<App />, document.querySelector("#root"));
<script src="https://unpkg.com/react#17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/ramda#0/dist/ramda.min.js"></script>
<div id="root"></div>
To avoid every time to create a copy of this.state.element you can use update with $set or $push or many others from immutability-helper
e.g.:
import update from 'immutability-helper';
const newData = update(myData, {
x: {y: {z: {$set: 7}}},
a: {b: {$push: [9]}}
});
setState trigger re rendering of the components.when we want to update state again and again we must need to setState otherwise it doesn't work correctly.
My current understanding is based on this and this answers:
IF you do not use shouldComponentUpdate or any other lifecycle methods (like componentWillReceiveProps, componentWillUpdate, and componentDidUpdate) where you compare the old and new props/state
THEN
It is fine to mutate state and then call setState(), otherwise it is not fine.
From React documentation.
Conceptually, components are like JavaScript functions. They accept
arbitrary inputs (called “props”) and return React elements describing
what should appear on the screen.
Considering:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
or
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
Will give us the ability to do this:
<Welcome name="Luke" />;
<Welcome name="Leia" />;
to use as we wish in the DOM,
Hello, Luke
Hello, Leia
Now when people prescribe props shouldn't be changed, it would make sense the reason is in my thinking would be like the same as changing the values of attributes of an image tag?
HTML:
<img id="Executor" alt="Picture of Executor" src="/somepath/vaders-star-destroyer-executor.jpg"/>
JS:
Meanwhile in a Javascript file a long time ago in a galaxy far, far away...
var imageOfVadersStarDestroyer = document.getElementById('Executor');
imageOfVadersStarDestroyer.src = "/somepath/vaders-star-destroyer-avenger.jpg"
Because if we keeping changing an elements attribute values this can cause confusion and slower renderings?
So is the reason why the prescription is to never change props in React is because is the library is trying to make elements as predictable as possible?
Setting props outside of React is dangerous and should be avoided. Why? The main reason is that it doesn't trigger re-renders. Hence bugs and unexpected behaviour.
Re-rendering
Most of the time, props are data that is store as state in the parent component, which is manipulated by calling setState() (or the second function returned by React.useState()). Once setState() is called, React re-renders and computes what has changed under the hood, with the latest props and state. Manually assigning values to props, therefore won't notify React that the data has changed and something has to be re-rendered.
The good practice
Making props read-only allows React components to be as pure as possible, which is obviously a good practice anyway even when writing plain JS. Data won't be changed unexpectedly and can only be done so by calling setState() (You might have heard of the single source of truth, which is what React is trying to leverage).
Imagine you notice something went wrong in the app and the data shown to the end user is completely different from the source, it would be a pain trying to find out where the data has been manipulated wouldn't it? :)
never change props in React
means that you should never do this.props.name = "userName" because of React's one way data binding, props are read only, to update a component's props, you should pass a function from the parent that will do that ( in the parent ) , or dispatch an action if you're using redux, a change in the props will trigger a re-render
props is a constant in this case. You will always need it in your components.
But there is a cleaner way to write it or even omit it.
Regular way with Function Expression (same as your exemple)
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
ES6 Object Destructing - explicit
function Welcome(props) {
const {name} = pros
return <h1>Hello, {name}</h1>;
}
ES6 Object Destructing - inplicit, cleaner way
function Welcome({name}) {
return <h1>Hello, {name}</h1>;
}
And of course, you can use the class way which requires the usage of this.props.yourAttr
However, in the new version 3 of create-react-app, changed class components to functional components. You can see this exact modification on Github here.
You can need to learn more about destructing assignment in the old and good MDN linked here or an in-depth approach both array and object destructuring here.
With react hooks now available should I in case of functional components wrap every function passed with props with useCallback and every other props value with useMemo?
Also having custom function inside my component dependent on any props value should I wrap it with useCallback?
What are good practices to decide which props or const values from component wrap with this hooks ?
If this improves performance why not to do it at all times ?
Lets consider custom button where we wrap click handler and add custom logic
function ExampleCustomButton({ onClick }) {
const handleClick = useCallback(
(event) => {
if (typeof onClick === 'function') {
onClick(event);
}
// do custom stuff
},
[onClick]
);
return <Button onClick={handleClick} />;
}
Lets consider custom button where we wrap click handler and add custom logic on condition
function ExampleCustomButton({ someBool }) {
const handleClick = useCallback(
(event) => {
if (someBool) {
// do custom stuff
}
},
[someBool]
);
return <Button onClick={handleClick} />;
}
Should i in this two cases wrap my handler with useCallback ?
Similar case with use memo.
function ExampleCustomButton({ someBool }) {
const memoizedSomeBool = useMemo(() => someBool, [someBool])
const handleClick = useCallback(
(event) => {
if (memoizedSomeBool) {
// do custom stuff
}
},
[memoizedSomeBool]
);
return <Button onClick={handleClick} />;
}
In this example I even pass memoized value to useCallback.
Another case what if in the component tree many components memoize same value ? How does this impact performance ?
Not worth it, for multiple reasons:
Even official docs say you should do it only when necessary.
Keep in mind that premature optimization is the root of all evil :)
It makes DX (developer experience) far worse: it's harder to read; harder to write; harder to refactor.
When dealing with primitives (like in your example) memoizing costs more CPU power than not doing it. A primitive value doesn't have a concept of references, so there's nothing to memoize in them. On the other hand, memoization itself (as any other hook) does require some tiny processing, nothing is for free. Even though it's tiny, it's still more than nothing (compared to just passing a primitive through), so you'd shoot your own foot with this approach.
To put all together - you'd waste more time typing all the hooks than a user would gain on having them in the application if you want to put them everywhere. The good old rule applies: Measure, then optimize.
I agree with the principles proposed by #jalooc
To give some more insight about the exhibited use cases in the OP, here is my advice:
Expensive children renders
function Component() {
const callback = useCallback(() => { dostuff }, [deps])
return <Child prop={callback} />
}
The above makes sense if Child is a very expensive component to render. As such, it is probably exported like so:
function Child() {
...this takes significant CPU...
}
// Export as a pure component
export default React.memo(Child)
Expensive computations
function Component({ foo }) {
// This very expensive computation will only run when it's input (foo)
// changes, allowing Component to re-render without performance issues
const bar = useMemo(() => {
... something very complicated with `foo` ...
}, [foo])
return <div>{bar}</div>
}
Conclusion
Do what makes sense or that have measured bad performance
A function declaration inside a component changes at each render. If this causes derived expensive computations, memoize it (useCallback) or move it outside the scope.
If a component itself is expensive to render, make it pure with React.memo, with the help of #2 if necessary
If something IS itself expensive to re-compute, memoize it (useMemo)
Do what makes sense or that have measured bad performance
I'd argue that there's nothing wrong with putting useCallback everywhere you create a function. The same points are applicable to useMemo; but I'll be mentioning only useCallback since, for the sake of brevity.
As a concession, I must mention that this isn't a cornerstone in your development process, and you must proceed with a solution that your team is comfortable with, whether it's using useCallback or not.
My main argument for using such memoization hooks "extensively" is that you don't have to think of potential performance issues on the front of "reference update-induced" rerendering if you do it. Fewer things to think about = good. More time and energy to solve the actual problems.
Many say "premature optimization is bad". Well, cargo cult isn't good either, and this citation is a pure example of such, taken too far out of context, and with a premise of having an authority behind it. here used to be a good summary on that, unfortunately, the account is taken down currently, but it's available on archive.org.
The concerns of "premature optimization" in this case are structural changes to the code and impaired readability/write-ability.
Structurally, these changes are for the better, making you separate your components.
Readability-wise, there's an extra wrapper, but that comes together with strict tracking of your function dependencies, which otherwise is done implicitly. Dependencies become explicit as they should be since in this system they play a too important role to swipe it under the rug. Readability, therefore, only wins. Although it's harder to write now, the "ritual" is always the same. Readability is more important than the convenience of writing a function.
(Just make sure you use eslint to track your dependencies; it could be a headache when you forget to add a dependency and it gets cached.)
So, "write now, optimize later" - thanks, I'd pass. To me, it's a negligibly cheap optimization that is justified enough and is reasonable enough, if your team is ready to accept the arguments above. It's also fine if they aren't: the topic itself isn't something to die for. It's just a small quality-of-life tool.
I'm trying to understand stateless components and what the difference is between these examples:
class App {
render() {
return (
<div>
{this.renderAFunction('hello')}
</div>
);
}
renderAFunction(text) {
return (
<p>{text}</p>
);
}
}
and this:
class App {
render() {
return(
<div>
<RenderAFunction text='hello'/>
</div>
);
}
}
const RenderAFunction = ({text}) => (
<p>{text}</p>
);
Or if there is any difference at all?
Functionally, there is absolutely no difference. Both end up rendering a paragraph element, but there are other aspects to consider. There are three points to make (in my opinion) when examining both methods:
Reusability: You have to understand to separate components when you need to. If renderAFunction is just meant to generate some JSX based on, for example, an API request, then it's fine being in a method. But if you want to reuse it somewhere else, then separate it into it's own component. A huge part of React is component reusability and getting rid of code duplication. Separating the method into it's own component would be imperative to accomplish this.
Purpose: There are reason to use stateless function components and reasons not to. The whole point of stateless functional components is to not have state and be presentational. If you need to do something that involves the React lifecycle or internal state, keep it as a method, or new class depending on if you want it reusable.
Performance: Using a stateless functional component would be less efficient. This is because it's a component, not just some JSX returned from a method. As of right now, the React team plans on making some optimizations for stateless functional components because they do not contain state and are merely presentational, but this probably won't happen until after React Fiber is done and thus your stateless functional component has no optimizations versus a regular full-fledged class component. That makes it incredibly inefficient versus a method returning some JSX, especially if it's just used once in another component.
A good rule of thumb is to ask yourself, do I need it anywhere else? If not, then keep it in a method. If you don't need it anywhere else, separating the JSX into a separate component would have worse performance and wouldn't follow React's core principles.
If you are going to need it somewhere else, then separate the component so you follow React's concept of reusability.
Your App's render function will be translated into following JS code for your first example:
render() {
return React.createElement(
'div',
null,
this.renderAFunction('hello')
);
}
And the following one for the second one:
render() {
return React.createElement(
'div',
null,
React.createElement(RenderAFunction, { text: 'hello' })
);
}
While they both looks almost the same, there is one significant difference: laziness. React will execute RenderAFunction body only in case it gonna be mounted to the DOM hierarchy.
It is insignificant is your case, but imaging following example:
const LazyApp = () => {
const heavy = <div><HeavyStuff /></div>
// ...
return <div>done it</div>
}
const HardWorkingApp = () => {
const heavy = <div>{HeavyStuff()}</div>
// ...
return <div>done it</div>
}
const HeavyStuff = () => {
// ... do heavy rendering here
}
Lazy app will not perform any heavy stuff at all. Maybe it is not very clear from that synthetic example why one would do anything like that at all. But there are real-world examples out there, when something conceptually similar (creating Components without rendering them) is happening.
Basically, both will serve the same purpose. But the real advantage of creating a component is its reusability.
If you have to render that particular piece of jsx just for this component. Then, there is no need to create a separate component.