January 15, 2019
So as previously stated one of my side projects is Hooked-Form, I’ve been able to put quite some work into this project. I like the hooks api a lot, reasoning in hooks makes everything a lot more structured in my opinion. That all aside it gives a big plus in bundle sizes when not needing to resort to classes.
This being said I’m a true fanatic when it comes to optimising what I make, that’s why I’ve been diving into the observed bits API of React itself, which pretty much came up empty handed for me. I’ll probably attempt it again at some later stage but yes for now I think I’ve got what I wanted out of this experiment.
What’s most commonly referred to as THE pitfall for forms is that when a value updates, logically
values object will cause a rerender. This implies that every context observer
(in this case the Fields) will get notified about this. Resulting in the reevaluation of the lifecycles.
Often the structure of the props passed to the actual input isn’t flat and
PureComponent won’t bail you
out of a partial rerender.
This could be caused by a fresh Object (array or real object) or passing a function (.bind or inline arrow) to the component, this causes them to never be equal by refference and thus making your PureComponet ineffective.
In this part I’ll try to shine some light on how I got to the solution and as to why every Field will be implementing this optimization by default.
When I approached this problem I realised the problem was situated in the way props are passed from parent -> Field -> input, this might seem really obvious but there’s a small catch to it. So I started thinking about a way to enable the user to bust the memoization AND to make the library prevent the most common pitfalls.
Since hooks are becoming a thing now we’ve got the perfect toolkit to do this.
My first point of order was testing if
useRef triggers a rerender even if my component was fully shielded from ever
rerendering. This was the case, so the emitting was 100% the same as for a Context.Consumer.
Secondly I had to test if returning a memoized component would yield good results, which also happened to be the case, at this point I was fully ready to start experimenting with this.
First I had to build a way to never rerender, this was the premise for my small experiment to yield results,
this was easily done by doing
const Field = React.memo(() => component, () => true) since the second argument
of memo is named
areEqual when passed true it does not rerender.
The next part was making sure the return of my component was memoized and bustable, this could be done in line with the above example
return React.useMemo(() => React.createElement(component, props), [value, touched, error]);
This also happened to be the point where I started realising how viable this idea was to make Field like this by default, more on that later though, this only needed one more thing and that was a way for the user to make parent props cause a rerender.
This could easily be done by allowing the user to pass an array of props to listen on. That’s when the
came to life.
I also opted to make this default to
['disabled', 'className'] since these are the two properties that are the same for every
Since this essentially is a hooks-form api I opted to make all
Field components performant since if the user would want to de-opt,
the user could just use the
useField function which essentially returns the same things but without the optimizations.
At the point of writing this I’ve got a three day holiday coming up where I plan to fully test this idea, along with finishing up a side project and finally finishing my graphql-caching project.
So there’s a chanche this post might get a small follow up about some caveats I did not think about with the approach I took.
I hope I shed some light on the thoughts behind how I make decisions and as to why I wanted to do this. I hope you enjoyed the read and would love to hear some feedback on idea’s like these.
I know some people will be quite sceptical about people having to worry about one less thing once more but I don’t think things like these make the developer less capable or whatever. It just gives them one less problem on their mind in a front-end world full of things to worry about.
Written by Jovi De Croock who lives and works in Belgium building mainly in React(-native) and Node.js. You should follow me on Twitter