Alternative to "mostly legacy" ref String Attribute? - reactjs

I was a bit surprised when I read the updated React docs about refs. While their examples on the React front page still use this.refs.textarea, they later "recommend" to not use this approach anymore.
But what's the alternative? I find too long inline code confusing and calling a function instead, like {() => this.initChartLibrary() } (the () => is required to not lose context to this) requires to build a method that gives the wrong impression of that it can be reused.
In other words, I'd still like to use the String way. But since I fear they'll deprecate it, I was wondering, if there are any other approaches?

As far as I'm aware the main alternative to ref strings is in fact the ES6 style callback function.
So:
<textarea ref="myTextArea">
...
</textarea>
// Where refs are then accessed by `this.refs.myTextArea` to perform any operations
Becomes, in a basic form:
<textarea ref={ (ref) => this.myTextArea = ref }>
...
</textarea>
// Where refs are then accessed by `this.myTextArea`
This can be pretty powerful because the ref attribute in React automatically receives the referenced component as a parameter, which allows us to immediately implement our callback operations on it if we'd like. This can be used in a simple form, as above, to assign that reference to this.myTextArea, or as you've probably noticed in the docs, you can be a bit more direct and do things like:
<input
ref = {(input) =>
{ if (input != null) {input.focus()} }
} />
That being said, although this sometimes means you can express your code in the fewest number of lines, it definitely sacrifices readability. If you are just looking to save a reference to the component for use in an event handler (as is very commonly the case) using a ref string is honestly still the cleanest way to do so.
I really wouldn't anticipate them to be deprecated without warning, as they are used pretty heavily in React & remain convenient, but if they ever are your best bet would be a straightforward refactor with a simple (ref) => this.myRef = ref then accessing your refs by this.myRef.

Related

React.forwardRef is already possible without it, so what's the use of it?

I'm confused on the point of React.forwardRef. As explained in its documentation, I understand that its main use is for a Parent Component to gain access to DOM elements of the Child Component. But I can already do that without even having to use it.
Here is a code example that you can plug into CodeSandbox and see that it works:
import React, {useRef, useEffect} from "react";
import "./styles.css";
const ChildComponent = (props) => {
useEffect( ()=> {
props.callbackFunction()
})
return(
<div ref={props.fRef}>
{"hello"}
</div>
)
}
export default function App() {
const callbackFunction = () => {
console.log("The parent is now holding the forwarded ref to the child div: ")
console.log(forwardedRef)
}
const forwardedRef = useRef(null)
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<ChildComponent name="gravy" callbackFunction={callbackFunction} fRef={forwardedRef}/>
</div>
);
}
Or here's the embed of this example. Honestly, I'm kind of new to this and I don't know exactly how embeds work and whether someone fiddling with the embed changes my original Sandbox or not, so I was hesitant to put it. But here it is.
Example Forwarding Ref
In the example, the parent App() component successfully passes a ref to the child which the child attaches to its rendered div. After it renders, it calls a callback function to the parent. The parent then does a console log where it proves that its forwarded ref now has a hold of the child's div. And this is all done without React.forwardRef.
So what then is the use for React.forwardRef?
You're absolutely right that you can do what you've described. The downside is that you're forced to expose an API (ie: the fRef prop) for it to work. Not a huge deal if you're a solo developer building an app, but it can be more problematic eg. if you're maintaining an open-source library with a public API.
In that case, consumers of the library won't have access to the internals of a component, meaning you'd have to expose it for them somehow. You could simply do what you're suggesting in your example and add a named prop. In fact, that's what libraries did before React 16.3. Not a huge deal, but you'd have to document it so people know how to use it. Ideally, you'd also want some kind of standard that everyone used so it wasn't confusing (many libraries used the innerRef naming convention), but there'd have to be some consensus around that. So all doable, but perhaps not the ideal solution.
Using forwardRef, passing a ref to a component just works as expected. The ref prop is already standardized in React, so you don't need to go look at docs to figure out how to pass the ref down or how it works. However, the approach you describe is totally fine and if it meets your needs, by all means go with that.
As mentioned in the docs , it's useful for highly reusable components, meaning components that tend to be used like regular HTML DOM elements.
This is useful for component libraries where you have lots of "leaf" components. You've probably used one like Material UI.
Example:
Let's say you're maintaining a component library.
You create a <Button/> and <Input/> component that maybe just adds some default styling.
Notice how these components literally are just like regular HTML DOM elements with extra steps.
If these components were made to be used like regular HTML DOM elements, then I expect all the props to be the same, including ref, no?
Wouldn't it be tedious if to get the button ref from your <Button/> component I'd have to get it through something like fRef or buttonRef ?
Same with your <Input/>, do I have to go to the documentation just to find out what ref to use and it's something like inputRef ? Now I have to memorize?
Getting the ref should be as simple as <Button ref={}/>
Problem
As you might know, ref will not get passed through props because, like key, it is handled differently by React.
Solution
React.forwardRef() solves this so I can use <Button ref={}/> or <Input ref={}/>.

Can I call an event handler with a parameter using inline declaration of the parameter without having the function be recreated on every render?

As I have understood it the following way to call an event handler is
+ compact and simple to read
-, but causes a new myWrapperFunc function to be created on every render
However, creating functions is cheap right? That that minus is insignificant, right?
Am I correct in my understanding that this way to pass an event handler with a parameter will not cause a new handler instance to be created on each render?
handler(event, val) {
...
}
<Component onClick={myWrapperFunc = (e) => handler(e, "myVal")}>
First of all, you are passing the function wrong. It should be:
<Component onClick={(e) => handler(e, "myVal")}>
If we talk about the main question, I'm not an expert but there is still not an agreement on this subject. There is a reality that your callback function is created in every render and this causes a performance loss. But how significant is this loss? This depends on your app probably.
Is it a big app which includes so many components that create callbacks like that. So, you should consider an optimization then. Some people say if you don't need optimization then don't bother with it :) Some says follow the best practices.
You can pass parameters to your functions without using them like that but somehow you should get these parameters in your component. If it is a prop then use it directly instead of passing it like that for example. Then use a separate function and its reference. You don't need to pass e for a callback, it is passed by automatically with callbacks.
handler () => {
use(event);
use(props.val);
use(val_variable_in_component;
...
}
..
<Component onClick={handler}>

Passing more parameters to a pure render function in React

Lately I've been trying to write my React components as "Pure Functions" and I've noticed that sometimes I want to have something which feels a lot like state. I was thinking about passing my state as a second parameter to my component. I can achieve this by calling my component as a normal function with two parameters, props and state.
For example:
// abstracted to it's own module
const useState = (Component, state = {}) => {
return class extends React.Component {
state = createState(this, state); // will traverse and update the state
render() {
const { props, state } = this;
return Component(props, state); // <-- call the Component directly
}
};
};
const Component = (props, { index, increase }) => (
<div onClick={increase} {...props}>
Click me to increase: {index}
</div>
);
const componentState = {
index: 0,
increase: (event, state) => ({ ...state, index: state.index + 1 })
};
const StatefullComponent = useState(Component, componentState);
<StatefullComponent style={{ color: "purple" }} />;
I have a CodeSandbox example:
My questions are:
Will this pattern harm performance?
I'm no longer extending the props with state values, this might be a good thing
I am messing with the way components are rendered by default, this might be a bad thing
Will this Pattern break things like shouldComponentUpdate? (I have a sinking feeling this is modelling the old context api)
How worried should I be that future react updates will break this code?
Is there a more "Reacty" way of using State in a Pure function without resorting to libraries like Redux?
Am I trying to solve something which should not be solved?
Note: I'm using state in this example, but it could also be a theme, authorisation rules or other things you might want passed into your component.
EDIT 19-03-2018: I have noticed that people seem to be confused about what I'm asking. I'm not looking for a new framework or a conversation about "why do you want to separate your concerns?". I am quite sure this pattern will clean up my code and make it more testable and "cleaner" in general. I really want to know if the React framework will in any way hinder this pattern.
At first glanced when I checked your code I had a question:
"Why do you make it so complicated? When you can simply make it with a class declaration".
But later when I have splitted your code I found it really worth to do that.
Question 1: Doesn't really make a difference, it is the way how HOC does the composition.
I'm no longer extending the props with state values, this might be a good thing
Why/When might it be a good thing?
I am messing with the way components are rendered by default, this might be a bad thing
I don't see that you break or mess the rendering by default, I think the HOC pattern promotes the same philosophy, the difference you separate state from props.
Question 2: If a developer decide to use a stateless component then he/she should realize all “lifecycle methods” or references ref will be not available.
Your pattern make stateless component as “statefull” but in stateless declaration - amazing 😋.
Like in JSX you write in JS an "HTML" and inside it JS code with another "HTML":
<ul>
{list.map(text => <li>text</li>)} // I know there should be used key
</ul>
Mr. Baudin pattern (state-full like stateless):
import React from 'react'
import {useState} from './lib'
const state = {
index: 0,
increase: (event, state) => ({index: state.index + 1})
}
const Component = (props, state) => (
<div onClick={state.increase} {...props}>
Click me to increase: {state.index}
</div>
)
export default useState(Component, state)
Question 3: It depends what break changes will be in coming versions.
Question 4: Well... I don't think the offered pattern (implemented library) can be considered as application state management but it can be used within any state management like Redux or Mobx because it deals with internal component state.
Question 5: No, I don't think. Your solution makes code less and clean. Functional components are good for very simple or representational components and now it can be extended with state.
While this question has been open I've done some painful research on the subject and I'd like to share this research with you.
Question 1: Performance; Calling your components as functions or even as constructor functions doesn't really make a difference. You simply get your component instead of a type.
// the component
const MyComponent = () => (<div>This is my page</div>);
console.log(MyComponent());
console.log(new MyComponent());
console.log(<MyComponent />);
console.log(React.createElement(MyComponent));
Pen (Don't forget to inspect the developer tools!)
What I've noticed is that when you call a component directly you lose a little information, for example, when I use JSX the type information is preserved:
React.createElement(MyComponent).type === MyComponent // <- true
MyComponent() // <- Now way to find out what constructed this...
This doesn't seem like a big deal because the MyComponent() is seen as a normal div so it should render correctly; but I can imagine that React might do some lookup on the type of the component and calling your function like this that might interfere with the performance.
Haven't found anything in the documentation nor in the source code to suggest that this is the case, so I see no reason to worry about performance at this point.
Question 2: Does this break shouldComponentUpdate; the answer is "maybe not", but not because I need to write a class as was suggested. The problem is that React does a shallow compare on the props when you use a PureComponent and with pure functions just expects that with the same props you get the same result. In my case, because of the second parameter it might think the component doesn't need to update but actually it should. Because of some magic in my implementation this seems to work for child components of a root component wrapped with the useState function.
This is as I expected the same problem as with the original implementation of the context api. And as such I should be able to solve it using some reactive techniques.
Question 3: Seeing how "just calling a component as a function" seems to be the entire idea behind react and seeing how it results in almost exactly the same component without the original type information I see no reason why this should break in the future.
Question 4/5: No, there is no more "Reacty" way of really solving this problem. There is how ever a more functional way. I could use a state monad and lift the entire thing up; but that would envolve a lot of work and I really can't see the benefit of doing that. Passing state as a second parameter seems, at least for now, as something which might be strange but viable and actually feasable.
Question 5: When I started looking around I didn't find a lot os answers to these questions, but now that I've really dug myself in I can see a few other libraries doing the same thing. For example: recompose which calls itself "lodash for react". They seem to use this pattern of wrapping your component in a function and returning a class a lot. (Their withState implementation).
Extra information: My conclusion is that this pattern (because it's nothing more than a pattern) is valid and does not break any fundamental rules of React. Just to give a little bit of extra information Bernardo Ferreira Bastos Braga wrote that I needed to use a class to do it "the React way". I fail to see how wrapping your function and returning a class with state is anything other than "using a class".
I do however realise that wrapping a function increases complexity, but not by much; function calls are really optimised and because you write for maintainability and optimise later.
One of my biggest fears is that when the software gets more and more complocated and we get more cross-cutting concerns to deal with, it will get harder and harder to handle every concern as a parameter. In this case it might be good to use a destructuring pattern to get the concerns which you need from a "concerns" obejct passed as the second parameter.
One last thing about this pattern. I've done a small test (Just selenium rendering a page a 100 times) and this pattern, on a small scale, is faster than using Redux. The bigger your redux state gets and the more components you connect the faster this pattern becomes. The down side is that you are now doing a bit of manual state management, this comes with a real cost in complexity. Just remember to weigh all options.
A few examples of why this state component
Applications which interact with users, require that you try to keep track of the interactions they have. These interactions can be modeled in different ways but I really like a stateful approach. This means that you 'thread' state through your application. Now in react you have a few ways of creating components. The three I want o mention are:
create a class and extend from Component
create a class and extend from PureComponent
create a stateless function
I really like the last option but, to be honest, it's a pain keeping your code performant. There are a lot of articles our there explaining how lambda expression will create a new function every time your component is called, breaking the shallow compare of the props done by PureComponent.
To counteract this I use a pattern where I wrap my stateless component in a HoC where I pass my component and my state object. This HoC does some magic and passes the state as a second parameter to the stateless function, ensuring that when the props are tested by the compare of the PureComponent it should work.
Now to make the wrapper even better I memoize the lambdas so that only a single reference to that function exists so that even if you were to test the function by reference it should still be OK.
The code I use for this is:
return Object.entries(_state).reduce(
(acc, entry) => {
const [key, value] = entry;
if (value instanceof Function) {
acc[key] = _.memoize(item => (...args) => {
const newState = value.apply(null, [...args, root.state, root.props]);
root.setState(newState);
});
} else {
acc[key] = value;
}
return acc;
},
{}
);
};
As you can see I memoize the function and call it proxying the arguments and passing in the state and the props. This works as long as you can call these functions with a unique object like so:
const MyComponent = useState((props, { items, setTitle }) => {
return (
<div>
{items.map(item => (
<Component key={item.id} item={item} changeItem={setTitle(item)} />
))}
</div>
);
}, state);
1- Will this pattern harm performance?
Performance is usually not a black/white, rather it is better / worse in different scenarios. Since React already has a standard way of doing this, it it plausible that you'll be missing out on internal optimizations.
2-Will this Pattern break things like shouldComponentUpdate? (I have a sinking feeling this is modelling the old context api)
Yes, you should be using the class declaration if you need to write shouldComponentUpdate functions
3- How worried should I be that future react updates will break this code?
I think is fair to say that you should, since there are obvious and documented ways of doing the same using classes.
4 - Is there a more "Reacty" way of using State in a Pure function without resorting to libraries like Redux?
you could have a container component that has state and pass down callback functions to update the state
5- Am I trying to solve something which should not be solved?
yes, since there is already a mainstream and documented way of archieving what you need using the Component class. You should probably resort to functional components only for very simple or presentational components

What's the actual cost of defining new function within react render method?

I'm trying to clarify the pros/cons of having a new function declaration within react's render method.
Consider a render method like the following:
render () {
return (<SomeComponent somePropWithoutEventBind={() => console.log('not so dangerous?')} />)
}
In the above example, somePropWithoutEventBind doesn't bind to a DOM event: react will check for prop changes and every time render is called this prop has changed - because it's a new function - so it never matches the previous, this is expensive but nothing tremendous.
Now in this case
render () {
return (<input onChange={() => console.log('dangerous?')} />)
}
onChange prop does bind to DOM (doing something like addEventListener) so every render will have to removeEventListener and addEventListener again? Is this the main reason behind avoiding to declare functions inside the render method?
If possible, please justify your answer pointing to react source code.
The main reason of avoiding defining new functions in render is to avoid over rendering.
Consider bind a new function onto a DOM element (react element not real DOM) like so: <button onClick={_ => this.setState({ hide: true })}>Hide Me</button>} there's almost none cost at all, since DOM elements gets re-rendered anyways. (site note: react doesn't use native DOM events like add/removeEventListener, it uses SyntheticEvent and your code targets virtual DOM aka react element not real DOM)
However for a custom components (In large codebase we typically have lots of complex Container Component composed of Functional/Class Child Components. Let's say you have something like
render() {
// you won't run into unnessary re-render issue
// when you use `onClick={this.handleClick}` since the function reference doesn't change
// while most perf tricks done by react bindings are relying on shallow compare of props/state
return (
<ComplexContainer onClick={_ => this.setState({ forceReRender: true})}>
<Child1 />
<Child2>
<NestedChild1 />
<NestedChild2 />
</Child2>
</ComplexContainer>
)
}
If you do this way, this will cause the whole render tree starting from ComplexContainer to re-render, this may have notable negative perf impacts, but you will need DevTools profiling to benchmark.
In fact, the real thing i wanna say is: it might not be that huge as you concern, avoid premature optimization can be more important. Give this awesome reading material a shot: React, Inline Functions, and Performance
A bit more info regarding react synthetic event system here, it's simply a wrapper of native DOM events to normalize the subtle differences of events among different browser vendors. The API would be the same event.preventDefault()/event.preventPropagation() etc works as it is, but you get cross-browser compatibility for free. Regarding how it works internally please see event delegation

React - How to properly pass and invoke functions that accepts parameters to a component

I'm using react and I have read a lot of articles about how to use it properly and avoiding unnecessary re-rendering, and one of the topics was the way you pass functions to a component. So basically we need to avoid passing a different instance of a function like this:
<Component onClick={() => ... }/>
or
<Component onClick={this.method.bind(this)} />
and you should bind in the constructor the function and just pass as following:
this.method = this.method.bind(this);
<Component onClick={this.method} />
Which makes perfectly sense, but now, I have a method that accepts a parameter:
onPropertyChange(field, event) {
...
}
And I want this method to be invoked by an input whenever the value changed, the only way that I managed to do it is this:
<input onChange={this.onPropertyChange.bind(this, 'id')} />
That works perfectly since it binds the function to my component, passes the field and the event.
But in this way it will pass a different instance each time, which will cause unnecessary re-rendering.
So how can I do it properly and avoid unnecessary re-rendering.
The answer to your question really depends where id comes from.
If you have several fixed strings and want to avoid the "overhead" of creating a new anonymous function once per render, then you can create several bound copies in the constructor:
this.onIdChange = this.onPropertyChange.bind(this, 'id');
this.onNameChange = this.onPropertyChange.bind(this, 'name');
Then use:
<input onChange={this.onIdChange} />
I'd be wary of premature optimisation in this case, though. If you haven't determined for definite that creating anonymous functions in your render methods is killing the performance of your application, then I wouldn't assume it to be the case.
Note that your code:
<input onChange={this.onPropertyChange.bind(this, 'id')} />
is not triggering unnecessary re-renders. The only thing it is doing which could be perceived as "bad practice" is creating a new function every time a re-render is triggered. This will create some overhead but you should really do some profiling before assuming that it's going to cause problems.
Sorry this is a bit of a non-answer, but I think the answer is that this:
<input onChange={this.onPropertyChange.bind(this, 'id')} />
or this
<input onChange={() => this.onPropertyChange('id')} />
is the "proper" way to do it.
Best practices are good, and the theory is sound, but is this premature optimisation? Or unquantified optimization?
Here's my performance tuning checklist. (TL:DR, time everything, fix what makes a difference.)
Install and run React's perf tools to identify components that are rendering unnecessarily.
If you have many components, there will be some that are rendering when they don't need to, but might only be costing one or two milliseconds. You can ignore them unless you envisage the number of them growing by an order of magnitude, or have nothing else to do.
For the components that are wasting tens of milliseconds (or might one day), implement shouldComponentUpdate checks to ensure you only run render() when a change is required. Make sure you're checking all props that a child might use. Remember these can be a gotcha if you add data to your model later, you'll need to add that to your checks to ensure render runs.
Complexity is where the bugs hide, so if you're doing something that feels clunky and can't say exactly why, I would suggest that you shouldn't be doing the clunky thing.

Resources