Take the following example code:
class Something extends Component {
render() {
return (
<SomeProvider
render={providedProps => (
<SomeChild {...providedProps}/>
)}
/>
)
}
}
Every React render props article uses this pattern as an example, but it's generally bad practice to define functions inline. Is this an exception to that rule?
Is there any benefit to defining the function outside of render?
class Something extends Component {
renderSomeChild = providedProps => (
<SomeChild {...providedProps}/>
)
render() {
return (
<SomeProvider
render={this.renderSomeChild}
/>
)
}
}
At this "function outside case" the const renderSomeChild (i suposed it is cause you did'nt mention) just make a reference to an allocated function in memory. So, if you aren't planning to reuse that function it make no sense to use it outside, i really think this make no significant on performance...
A way to use the first example as the best practices is to write a code like this:
class Something extends Component {
render() {
return (
<SomeProvider>
{providedProps => (
<SomeChild {...providedProps}/>
)}
</SomeProvider>
)
}
}
I hope the above code will help you! :)
My suspicion was correct that the renderProps function should not be re-declared within the render function - or else it will force re-renders. I confirmed this Why Did You Render?
Related
So after reading a few articles on react native performance, I realized that you're supposed to avoid arrow functions inside JSX.
https://blog.codemagic.io/improve-react-native-app-performance/#avoid-arrow-functions
But I have the following react-native code
function Layout() {
function handlePress(index) {
console.log("Inside HandlePress", index)
// I want index here
}
function NavItemIterable(item, index) {
return(
// how to pass index without arrow functions
<Pressable key={index} onPress={handlePress}>
<NavItem />
</Pressable>
)
}
return(
<Box>
{data.map(NavItemIterable)}
</Box>
)
}
With arrow functions, I could do something like
<Pressable key={index} onPress={()=> handlePress(index)}>.
How can I avoid an arrow function is this case & still call handlePress with index.
(or should I even try to avoid)
That blog post you have mentioned is at least misleading, and doesn't give sufficient context to understand it's statement (e.g. why you would even use a nested function in that case at all).
Your example (a function component) is fundamentally different to the one in that blog post (a class component).
In your example, it is ok to use a function to "pass" the parameters to the handler.
That is generally inline with common react principles, and it doesn't matter a lot if you are using
an arrow function (const f = () => {};) or a classic function (const f = function(){};). (optimizations are still possible.)
What the blog post says, and doesn't say ...
The blog post states that you should "Avoid Arrow Functions", and gives an example.
In that example there is a difference between using a classic and an arrow function, but that isn't the actual problem,
and the blog post doesn't say why you would use an extra function there at all (no matter if classic or arrow function) ?
class MyClass extends React.Component {
addTodo() {}
render() {
return <>
<MyComponent onClick={ () => this.addTodo() /* <-- Why would you even do this ? ... */ } />
<MyComponent onClick={ function(){ this.addTodo() } /* <-- ... even with a classic function ? */ } />
<MyComponent onClick={ this.addTodo } /> /* <-- Recommended. */
</>;
}
}
Of course, you should avoid that extra function, if there is no reason to add it.
What the blog post probably means ...
A common reason to use an extra function there is to capture the this context of the class. Indeed, here you need to use an arrow function.
In this example, only the arrow function will work (and the alternative), because it captures the this context of the component:
class MyClass extends React.Component {
myValue = 'some value';
addTodo(){
try { console.log( 'ok, value is:', this.myValue ); }
catch(_){
console.error( 'error: `this` is:', this );
}
}
render() {
const capturedThis = this;
return <>
<button onClick={ () => this.addTodo() } > arrow: works. </button>
<button onClick={ this.addTodo } > no extra function: error. </button>
<button onClick={ function(){ this.addTodo(); } } > classic function: worse error! </button>
<button onClick={ function(){ capturedThis.addTodo(); } } > classic alternative: works. </button>
</>;
}
}
What the blog post missed ...
The captured this context ("closure") might cause problems. That is probably why the blog post recommends to avoid it.
But the actual recommendation should not be to "avoid arrow functions", but to avoid capturing the this context in a closure (which is usually done by using an arrow function).
A solution would be to bind the method to the component:
class MyClass extends React.Component {
constructor(props){
super(props);
this.addTodo = this.addTodo.bind(this); // <-- bind method to the components `this`
}
myValue = 'some value';
addTodo(){
console.log( 'ok, value is:', this.myValue );
}
render() {
return <>
<button onClick={ this.addTodo } > works without arrow function </button>
</>;
}
}
Remark:
I would recommend that you just should forget that you have ever read the misleading recommendation in that blog post, and find better sources of knowledge.
I think closures, the .bind(this) pattern and optimizing functional components are beyond the scope of this question topic.
If you still want to optimize your functional component, unrelated to that blog post, then I think you should ask a new question.
I'm building a component that requires its direct descendants to be at least one of two types of components (ValidChildA or ValidChildB).
Using the most upvoted comment on this answer, I came up with this solution:
const ValidChildA = () => <div></div>;
const ValidChildB = () => <div></div>;
const Parent: React.FC<ParentProps> = ({ children, ...passedProps }) => {
React.Children.forEach(children, child => {
const isValidChildA = child.type.prototype instanceof ValidChildA;
const isValidChildB = child.type.prototype instanceof ValidChildB;
console.log(isValidChildA, isValidChildB); // Outputs false every time.
if(!isValidChildA && !isValidChildB){
console.error("Parent component expects only ValidChildA or ValidChildB as direct children.");
}
});
return <div {...passedProps} />
};
export default function Page(){
return (
<Parent>
<ValidChildA />
<ValidChildB />
</Parent>
);
};
Now despite the children being valid, my error is still thrown and thus the component doesn't render correctly.
I could use the displayName property to check if a child is a valid component, but since Next uglifies the code and the displayName property in production, this would break the app.
After a bunch of digging and reading (list included below), it seems like you could use named functions and then you could check the function name to see if it matched. I'm not sure if this will satisfy your requirements, but I did just try the following:
Define your ValidChild* components with function instead of the fat-arrow syntax () => .
function ValidChildA() {
return <div>A</div>
}
function ValidChildB() {
return <div>B</div>
}
Then in your checks inside the children loop, you can look at the function name and make sure it's valid.
React.Children.forEach(children, (child) => {
const isValidChildA = child.type?.name === ValidChildA.name;
const isValidChildB = child.type?.name === ValidChildB.name;
...
With this code, this component does not log the error
<Parent>
<ValidChildA />
<ValidChildB />
</Parent>
But this one does
<Parent>
<div>a</div>
<ValidChildA />
<ValidChildB />
</Parent>
The short answer ends here but I below I added some other thoughts and research that I did to get me here.
The reason what you're trying doesn't work (I think) is because
functional components don't have a prototype - they are not classes - they are functions. So if you look at child.type.prototype, you'll find it's undefined which of course will never be an instanceof anything.
I did a bunch of experimenting and found that even if you moved to class based components, these checks don't work. Seems kind of strange but...
class ValidChildB extends React.Component {
render() {
return <div>Bprime</div>
}
}
If we render this and add some logs...
React.Children.forEach(children, child => {
console.log(`Prototype ${child.type.prototype}`);
console.log(`instanceof object? ${child.type.prototype instanceof Object}`)
console.log(`instanceof Component? ${child.type.prototype instanceof React.Component}`)
console.log(`instanceof ValidChildB? ${child.type.prototype instanceof ValidChildB}`)
...
})
we get
// for ValidChildA - functional component
"Prototype undefined"
"instanceof object? false"
"instanceof Component? false"
"instanceof ValidChildB? false"
// for ValidChildB - class component
"Prototype [object Object]"
"instanceof object? true"
"instanceof Component? true"
"instanceof ValidChildB? false"
I did a bunch of reading and a Typescript guard solution (telling typescript to enforce that the children are ValidChildA | ValidChildB is tricky or not doable. Also, as you pointed out, doesn't solve your issue because you need it to happen at runtime. But still found it an interesting little research project. Check out the posts if you want more on that direction.
SO: How do I Restrict Type of React Children
SO: Typescript and React Children
I'm sorry this title might be a bit of a head-scratcher, I just can't quite think of how exactly to phrase my issue (open to any suggestions).
Basically there are 3 components: parent, ChildA, and ChildB
Parent renders both children, ChildB has a ref tag, Parent uses that ref tag to pass ChildB's function to ChildA.
class Parent extends Component {
render() {
<ChildA openChildB={() => this.childb.open()} />
<ChildB ref={instance => { this.childb = instance; }} />
}
}
Pretty basic.
The issue I am running into is that when ChildA executes that function it needs to pass an argument. I can't seem to figure out the correct way to do that.
I tried using a different syntax to pass the function down to ChildA -
<ChildA openChildB={this.childb.open} />
but that results in an error, Can't Read Property Of Undefined.
How can I pass a variable through this function?
Any help would be very appreciated!
Edit: I know I could pass the argument up to Parent and then from there place it in the function () => this.childb.open(arg) but for the sake of organization I would really prefer to handle that all within ChildA.
You can use the following:
class Parent extends Component {
render() {
<ChildA openChildB={(arg1) => this.childb.open(arg1)} />
<ChildB ref={instance => { this.childb = instance; }} />
}
}
And inside ChildA you should make sure to pass the relevant argument when you call the this.props.openChildB:
function handleOpenOnB() {
// This is an example, you should use the relevant value you want to pass to the openChildB function
this.props.openChildB(this.state.val);
}
I'm trying to convert this cool <canvas> animation I found here into a React reusable component. It looks like this component would require one parent component for the canvas, and many children components for the function Ball().
It would probably be better for performance reasons to make the Balls into stateless components as there will be many of them. I'm not as familiar with making stateless components and wondered where I should define the this.update() and this.draw functions defined in function Ball().
Do functions for stateless components go inside the component or outside? In other words, which of the following is better?
1:
const Ball = (props) => {
const update = () => {
...
}
const draw = () => {
...
}
return (
...
);
}
2:
function update() {
...
}
function draw() {
...
}
const Ball = (props) => {
return (
...
);
}
What are the pros and cons of each and is one of them better for specific use cases such as mine?
The first thing to note is that stateless functional components cannot have methods: You shouldn't count on calling update or draw on a rendered Ball if it is a stateless functional component.
In most cases you should declare the functions outside the component function so you declare them only once and always reuse the same reference. When you declare the function inside, every time the component is rendered the function will be defined again.
There are cases in which you will need to define a function inside the component to, for example, assign it as an event handler that behaves differently based on the properties of the component. But still you could define the function outside Ball and bind it with the properties, making the code much cleaner and making the update or draw functions reusable:
// you can use update somewhere else
const update = (propX, a, b) => { ... };
const Ball = props => (
<Something onClick={update.bind(null, props.x)} />
);
If you're using hooks, you can use useCallback to ensure the function is only redefined when any of its dependencies change (props.x in this case):
const Ball = props => {
const onClick = useCallback((a, b) => {
// do something with a, b and props.x
}, [props.x]);
return (
<Something onClick={onClick} />
);
}
This is the wrong way:
const Ball = props => {
function update(a, b) {
// props.x is visible here
}
return (
<Something onClick={update} />
);
}
When using useCallback, defining the update function in the useCallback hook itself or outside the component becomes a design decision more than anything: You should take into account if you're going to reuse update and/or if you need to access the scope of the component's closure to, for example, read/write to the state. Personally I choose to define it inside the component by default and make it reusable only if the need arises, to prevent over-engineering from the start. On top of that, reusing application logic is better done with more specific hooks, leaving components for presentational purposes. Defining the function outside the component while using hooks really depends on the grade of decoupling from React you want for your application logic.
Another common discussion about useCallback is whether to always use it for every function or not. That is, treat is as opt-in or always recommendable. I would argue to always use useCallback: I've seen many bugs caused by not wrapping a function in useCallback and not a single scenario where doing so affects the performance or logic in any way. In most cases, you want to keep a reference while the dependencies don't change, so you can use the function itself as a dependency for other effects, memos or callback. In many cases the callback will be passed as a prop to other elements, and if you memoized it with useCallback you won't change the props (thus re-render) other components independently of how cheap or costly that would be. I've seen many thousands of functions declared in components and not a single case in which using useCallback would have any down side. On the other hand most functions not memoized with useCallback would eventually be changed to do so, causing serious bugs or performance issues if the developer doesn't recognize the implications of not doing so. Technically there is a performance hit by using useCallback, as you would be creating and additional function but it is negligible compared to the re-declaration of the function that always has to happen either you use useCallback or not and the overall footprint of React and JavaScript. So, if you are really concerned about the performance impact of useCallback versus not using it, you should be questioning yourself if React is the right tool for the job.
You can place functions inside stateless functional components:
function Action() {
function handlePick(){
alert("test");
}
return (
<div>
<input type="button" onClick={handlePick} value="What you want to do ?" />
</div>
)
}
But it's not a good practice as the function handlePick() will be defined every time the component is rendered.
It would be better to define the function outside the component:
function handlePick(){
alert("test");
}
function Action() {
return (
<div>
<input type="button" onClick={handlePick} value="What you want to do ?" />
</div>
)
}
If you want to use props or state of component in function, that should be defined in component with useCallback.
function Component(props){
const onClick=useCallback(()=>{
// Do some things with props or state
},[])
return <Something {...{onClick}} />
}
On the other hand, if you don't want to use props or state in function, define that outside of component.
const computeSomethings=()=>{
// Do some things with params or side effects
}
function Component(props){
return <Something onClick={computeSomethings} />
}
For HTML tags you don't need useCallback because that will handle in react side and will not be assigned to HTML
function Component(props){
const onClick=()=>{
// Do some things with props or state
}
return <div {...{onClick}} />
}
Edit: Functions in hooks
For the use function in hooks for example useEffect, my suggestion is defining function inside useEffect, if you're worried about DRY, make your function pure call it in hook and give your params to it.
What about hooks deps? You should/could add all of your params to hooks deps, but useEffect just needs deps which should affect for them changes.
We can use the React hook useCallback as below in a functional component:
const home = (props) => {
const { small, img } = props
const [currentInd, setCurrentInd] = useState(0);
const imgArrayLength = img.length - 1;
useEffect(() => {
let id = setInterval(() => {
if (currentInd < imgArrayLength) {
setCurrentInd(currentInd => currentInd + 1)
}
else {
setCurrentInd(0)
}
}, 5000);
return () => clearInterval(id);
}, [currentInd]);
const onLeftClickHandler = useCallback(
() => {
if (currentInd === 0) {
}
else {
setCurrentInd(currentInd => currentInd - 1)
}
},
[currentInd],
);
const onRightClickHandler = useCallback(
() => {
if (currentInd < imgArrayLength) {
setCurrentInd(currentInd => currentInd + 1)
}
else {
}
},
[currentInd],
);
return (
<Wrapper img={img[currentInd]}>
<LeftSliderArrow className={currentInd > 0 ? "red" : 'no-red'} onClick={onLeftClickHandler}>
<img src={Icon_dir + "chevron_left_light.png"}></img>
</LeftSliderArrow>
<RightSliderArrow className={currentInd < imgArrayLength ? "red" : 'no-red'} onClick={onRightClickHandler}>
<img src={Icon_dir + "chevron_right_light.png"}></img>
</RightSliderArrow>
</Wrapper>);
}
export default home;
I'm getting 'img' from it's parent and that is an array.
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
const a = () => {
setCount(count + 1);
};
return (
<div>
<p>You clicked {count} times</p>
<button onClick={a}>Click me</button>
</div>
);
}
export default Example;
I have some React components that use SVG, and can be contained within an SVG or not. Consequently, I'd like to have a simple way of letting them render inside svg or g, depending on a param, something like:
export default class SVGComponent extends React.Component{
get containerElement() {
return this.props.inSVG ? 'g' : 'svg';
}
render() {
return(<{containerElement}>
<text>Extendable component</text>
</{containerElement}>);
}
}
This doesn't work: you can't use template injection in JSX this way. Is there another way I could approach it? It seems like I might be able to do React.DOM[containerElement], but I gather React does not like mixing React.DOM and JSX, and I don't want to move entirely over to React.DOM syntax just for this one function.
Take a look at this answer Dynamic tag name in jsx and React
It seems you can create a variable with the first letter capitalized and use it like a component like this
const CustomTag = `h${this.props.priority}`;
<CustomTag>Hello</CustomTag>
If this.props.priority == 3 this will render <h3>Hello</h3>
You can do it like this
render(){
var content = <text>Extendable component</text>;
if(this.props.inSVG){
return (
<g>{content}</g>
);
}
else {
return (
<svg>{content}</svgg>
);
}
}
I would probably do something like this:
export default class SVGComponent extends React.Component{
get containerElement() {
var content = (<text>Extendable component</text>);
return this.props.inSVG ? (<g>{content}</g>) : (<svg>{content}</svg>);
}
render() {
return({containerElement()});
}
}
But, it really depends on how dynamic the <text> is.
If the child elements are very dynamic, you might want to render this.props.children and add then as children of the component.