RecoilJS in practical - complex application
How I used RecoilJS in Refi App
Hi there, I just use Recoil for my own product for the first time.
So I decided to share my own experience when using Recoil 🤘
Why?
With the non-requirement of Refi App, it must be fast to bring the best DX so I need something to manage the state in a very optimized way
Why not Redux? Zustan? DVA?
- The boilderplace is so fukin hard. Besides, it really hard for optimizing to only render a component that subscribes to a sub-tree state. I need to use
selector
,memorize
,immutable
, ...
Why not MobX? MST?
- It has been a long time since I last use Mobx, MST. It's cool, but now the world has changed a lot, no more Class component, so I don't know it supports a lot.
- One more thing I don't want to use MobX is that the API is changed a lot in each major version. No! I don't want to use an outdated library for the rest of my life
- I feel can not control how Component will render when I use MobX
Why Recoil?
- It solves my concern, each component is subscribed to a very small state object (called atom) and only render once they changed
- It plays nice with Functional Component. You can easily change
useState
touseRecoilState
and vice versa. It's cool because "Alway use local state, only move it to global once needed" - I can map the product concept to state in my brain. A cell in a table should be an atom, so that the cell will render on its own.
How can I struct my state?
When using recoil, your state will build from pieces of atoms - A bottom-up approach. So that if you don't have a strategy for structuring it, you will end up with tons of atom
I decided to orders those atoms by
- firebase atoms
- navigator atoms
- UI atoms
- hotkeys atom
If it is biz state, I divided it by domain If it is for something to manage display, I divided it by its function
As you can see in the images, I also make a .action.ts
files. Once using recoil in a complex app, you will often need to update the state of many atoms for one action.
For eg: Once users click on the Commit
button, I will need to submit all the modified/new/deleted documents to the server, I also need to update a UI atom to show loading.
By splitting all actions into .action.ts
file. I can list out all the use cases and conveniently not mess my mind once add or edit something.
One more thing is all the atoms, selectors must postfix with Atom
. If not your brain will get confused when using it. Is this object is Atom Value, Atom State, or just a local state?
For that strategy, RefiApp tech about 60 atoms object so far. I'm happy with the current status but I think I gonna divide it for smaller if the app is grow
The funny parts
- As I write above, it really easy to convert from
useRecoilState
touseState
which is free my brain a lot. I don't need to ask myself, should I put it at global every time I try to introduce a new state. - An
atom
state can easily convert toselector
and vice versa. Why I will need that?
In the image above, I have a propertyListAtom
to store the propertys
that will show in each collection table. It will have an empty array []
as the default value. But I have a case that if users access a collection for the first time, I will generate some property
to put on that list so that I introduce a null
type for that atom. If I change the type of propertyListAtom
then I gonna need to find everywhere using that atom to make an update.
No, you don't need to do that!. I just add a new atom propertyListCoreAtom
and turn my old atom one to selector
. Really enjoy!
- Components will only render if its subscribed atoms update which is my own goal. No more energy to put on a stupid thing like
redux
andselector
...
The awful parts
- You have to write all the logic in a component, there is no official way to mutate a state from outside. I know they have reason to make it, but it feels poor for developers to follow it. But I found a way to eliminate that github.com/facebookexperimental/Recoil/issu..
- There are some issues on performance still not solve yet. The components will render if the
selector
value is not changed (their dependencies changed) github.com/facebookexperimental/Recoil/issu.. - I have a collection contains for about 100 documents. And I tried to update 100 atoms once I received the document data, the app is hanged. The recoil and react is not smart enough to batch those update! But it turns on an idea in my head 💡 which this approach I can batch update my app which out to do lots thing and the result is incredible. I called this approach is Separated tree timeline, and it also applicable for Redux, let's do it on the next post
- You can not subscribe to an
atom
value without making your component rerender. I solve that by introducing RxJS to my code, not that hard when combining them - The only debug tool that worked for me is
console
. Despise there some DevTool for Recoil but it's buggy and the DX is bad
Takeaways
- ⚡️ Recoil is fast and easy to use
- 🆙 It will boost productivity by "Use
useState
and convert it touseRecoilState
when needed" - 😤 You gonna need a strategy to struct your atoms because it will be lots more
- ⚠️ It still lacks some support on the advantage cases, you will need help for other libs.