Recoil
Kraków, 2020-08-25
Mateusz Bryła
Team Lead
mateusz.bryla@codete.com
menti.com
31 33 67 2
Agenda
● Meet the audience ;)
● Where we are with state management?
● Let’s listen to the story of ........................ - the Project Owner
● Recoil basics
● Live demo ;)
● Summary
● Further reading
● Q&A
State of the art of the management of the state
Flexibility,
Possibilities
Effort,
Overhead,
Learning curve
●
Redux
●
useState + props
●
React Context
●
Redux + Redux Toolkit
●
MobX
CC BY-ND 2.0 by Pierre Reveille
Let’s build a map app
App base
export const App = () => {
return (
<>
<Sidebar />
<Canvas />
</>
);
};
Cool feature: user draggable markers!
Markers support
export const App = () => {
const [markersIds, setMarkersIds] = useState([]);
return (
<>
<SideBar setMarkerIds={setMarkersIds} />
<Canvas>
{markersIds.map(id => <MapMarker id={id} key={id} />)}
</Canvas>
</>
);
};
export const MapMarker = ({ id }) => {
const [state, setState] = useState(createInitialMarkerState());
return createCanvasMarker(state, setState);
}
Cool feature #2: inputs and statistics!
Problem
What would be the React way?
What would be the ideal way?
Meet Recoil!
Where?
https://github.com/facebookexperimental/Recoil
(mind the “experimental” - it’s only v0.10!)
In numbers?
● created 2020-05-04
● 9k stars, ~400 forks, 40 contributors
How?
● yarn add recoil
● wrap your app with <RecoilProvider>
Why?
You’ll see in a minute ;)
2020-08-25, https://www.npmtrends.com/recoil
The Recoil way
Local state
export const SimpleMapMarker = () => {
const [latlng, setLatlng] = useState([0, 0]);
return <CanvasDraggableMarker latlng={latlng} setLatlng={setLatlng} />
}
export const SimpleMarkerInputFields = () => {
const [latlng, setLatlng] = useState([0, 0]);
return <LatitudeLongitudeNumberInputs latlng={latlng} setLatlng={setLatlng} />
}
State shared through Recoil atom
import { useRecoilState } from 'recoil';
import { markerPosition } from './MarkerState';
export const SimpleMapMarker = () => {
const [latlng, setLatlng] = useRecoilState(markerPosition);
return <CanvasDraggableMarker latlng={latlng} setLatlng={setLatlng} />
}
import { useRecoilState } from 'recoil';
import { markerPosition } from './MarkerState';
export const SimpleMarkerInputFields = () => {
const [latlng, setLatlng] = useRecoilState(markerPosition);
return <LatitudeLongitudeNumberInputs latlng={latlng} setLatlng={setLatlng} />
}
Recoil Atom
export const markerPosition = atom({
key: 'markerPosition',
default: [0, 0],
});
Recoil Atom (for real)
export const markerPosition = atom({
key: 'markerPosition',
default: [0, 0],
});
export const markerWithId = memoize((id) => atom({
key: `marker-${id}`,
default: [0, 0],
}));
Recoil Atom (for real)
export const markerWithId = memoize((id) => atom({
key: `marker-${id}`,
default: [0, 0],
}));
export const MapMarker = ({ id }) => {
const [state, setState] = useState(createInitialMarkerState());
return createCanvasMarker(state, setState);
}
export const MapMarker = ({ id }) => {
const [state, setState] = useRecoilState(markerWithId(id));
return createCanvasMarker(state, setState);
}
Cool feature #3: …?
Cool feature #3: GEOFENCING!
Cool feature #3: …?
Derived values - Recoil selector
export const geofence = selector({
key: 'geofence',
get: ({ get }) => {
const selectedMarkersIds = get(selectionAtom);
const selectedItems = selectedMarkersIds.map((id) => get(markerWithId(id)));
return createGeofence(selectedItems);
},
});
Async selector? No problem ;)
export const geofence = selector({
key: 'geofence',
get: ({ get }) => {
const selectedMarkersIds = get(selectionAtom);
const selectedItems = selectedMarkersIds.map((id) => get(markerWithId(id)));
return new Promise(createGeofence(selectedItems));
},
});
Cool feature #4: Persistence
https://my-amazing-map.com/?markers=[...],geofence=[...]
State observation
useRecoilTransactionObserver_UNSTABLE(({
snapshot,
}) => {
persist(snapshot.getLoadable(someAtom));
});
Recoil core concepts - summary
● Flexible shared state
● Derived data and queries
● App-wide state observation
● (built in asynchronous data handling)
● (built in React concurrent ready)
Demo time!
Should I bother?
If you…
● Need more than a simple Context can handle
● Don’t like to introduce too much overhead
● Are not scared by the “experimental” keyword…
● … but want to be ready for React Concurrent Mode
… then at least give it a try ;)
For under-the-hood addicts
● Works similar to react-redux
○ <Provider> exposes Redux internal tree structure, connect wraps with HOC
and allows data read (only when necessary) and manipulation
○ <RecoilRoot> exposes Recoil internal Atoms tree structure, hooks allow data
read (only when necessary) and manipulation
● There is more, check it out yourself ;)
For bettime readers
● Introduction to Recoil by its creator - Dave McCabe (@mcc_abe)
https://www.youtube.com/watch?v=_ISAA_Jt9kI
● Official Recoil docs (be aware of “This API is currently under development and will change”)
https://recoiljs.org/docs/introduction/core-concepts
● Recoil source
https://github.com/facebookexperimental/Recoil
● React concurrent mode
https://pl.reactjs.org/docs/concurrent-mode-intro.html
● This presentation
TODO PASTE LINK ;)
Q & A
Thank you!
Mateusz Bryła
Team Lead
@ mateusz.bryla@lingmates.com
Web lingmates.com
LinkedIn mateusz-bryla

Recoil at Codete Webinars #3

  • 1.
  • 2.
  • 3.
    Agenda ● Meet theaudience ;) ● Where we are with state management? ● Let’s listen to the story of ........................ - the Project Owner ● Recoil basics ● Live demo ;) ● Summary ● Further reading ● Q&A
  • 4.
    State of theart of the management of the state Flexibility, Possibilities Effort, Overhead, Learning curve ● Redux ● useState + props ● React Context ● Redux + Redux Toolkit ● MobX
  • 5.
    CC BY-ND 2.0by Pierre Reveille
  • 6.
  • 7.
    App base export constApp = () => { return ( <> <Sidebar /> <Canvas /> </> ); };
  • 8.
    Cool feature: userdraggable markers!
  • 9.
    Markers support export constApp = () => { const [markersIds, setMarkersIds] = useState([]); return ( <> <SideBar setMarkerIds={setMarkersIds} /> <Canvas> {markersIds.map(id => <MapMarker id={id} key={id} />)} </Canvas> </> ); }; export const MapMarker = ({ id }) => { const [state, setState] = useState(createInitialMarkerState()); return createCanvasMarker(state, setState); }
  • 10.
    Cool feature #2:inputs and statistics!
  • 11.
  • 12.
    What would bethe React way?
  • 13.
    What would bethe ideal way?
  • 14.
    Meet Recoil! Where? https://github.com/facebookexperimental/Recoil (mind the“experimental” - it’s only v0.10!) In numbers? ● created 2020-05-04 ● 9k stars, ~400 forks, 40 contributors How? ● yarn add recoil ● wrap your app with <RecoilProvider> Why? You’ll see in a minute ;) 2020-08-25, https://www.npmtrends.com/recoil
  • 15.
  • 16.
    Local state export constSimpleMapMarker = () => { const [latlng, setLatlng] = useState([0, 0]); return <CanvasDraggableMarker latlng={latlng} setLatlng={setLatlng} /> } export const SimpleMarkerInputFields = () => { const [latlng, setLatlng] = useState([0, 0]); return <LatitudeLongitudeNumberInputs latlng={latlng} setLatlng={setLatlng} /> }
  • 17.
    State shared throughRecoil atom import { useRecoilState } from 'recoil'; import { markerPosition } from './MarkerState'; export const SimpleMapMarker = () => { const [latlng, setLatlng] = useRecoilState(markerPosition); return <CanvasDraggableMarker latlng={latlng} setLatlng={setLatlng} /> } import { useRecoilState } from 'recoil'; import { markerPosition } from './MarkerState'; export const SimpleMarkerInputFields = () => { const [latlng, setLatlng] = useRecoilState(markerPosition); return <LatitudeLongitudeNumberInputs latlng={latlng} setLatlng={setLatlng} /> }
  • 18.
    Recoil Atom export constmarkerPosition = atom({ key: 'markerPosition', default: [0, 0], });
  • 19.
    Recoil Atom (forreal) export const markerPosition = atom({ key: 'markerPosition', default: [0, 0], }); export const markerWithId = memoize((id) => atom({ key: `marker-${id}`, default: [0, 0], }));
  • 20.
    Recoil Atom (forreal) export const markerWithId = memoize((id) => atom({ key: `marker-${id}`, default: [0, 0], })); export const MapMarker = ({ id }) => { const [state, setState] = useState(createInitialMarkerState()); return createCanvasMarker(state, setState); } export const MapMarker = ({ id }) => { const [state, setState] = useRecoilState(markerWithId(id)); return createCanvasMarker(state, setState); }
  • 21.
  • 22.
    Cool feature #3:GEOFENCING!
  • 23.
  • 24.
    Derived values -Recoil selector export const geofence = selector({ key: 'geofence', get: ({ get }) => { const selectedMarkersIds = get(selectionAtom); const selectedItems = selectedMarkersIds.map((id) => get(markerWithId(id))); return createGeofence(selectedItems); }, });
  • 25.
    Async selector? Noproblem ;) export const geofence = selector({ key: 'geofence', get: ({ get }) => { const selectedMarkersIds = get(selectionAtom); const selectedItems = selectedMarkersIds.map((id) => get(markerWithId(id))); return new Promise(createGeofence(selectedItems)); }, });
  • 26.
    Cool feature #4:Persistence https://my-amazing-map.com/?markers=[...],geofence=[...]
  • 27.
  • 28.
    Recoil core concepts- summary ● Flexible shared state ● Derived data and queries ● App-wide state observation ● (built in asynchronous data handling) ● (built in React concurrent ready)
  • 29.
  • 30.
    Should I bother? Ifyou… ● Need more than a simple Context can handle ● Don’t like to introduce too much overhead ● Are not scared by the “experimental” keyword… ● … but want to be ready for React Concurrent Mode … then at least give it a try ;)
  • 31.
    For under-the-hood addicts ●Works similar to react-redux ○ <Provider> exposes Redux internal tree structure, connect wraps with HOC and allows data read (only when necessary) and manipulation ○ <RecoilRoot> exposes Recoil internal Atoms tree structure, hooks allow data read (only when necessary) and manipulation ● There is more, check it out yourself ;)
  • 32.
    For bettime readers ●Introduction to Recoil by its creator - Dave McCabe (@mcc_abe) https://www.youtube.com/watch?v=_ISAA_Jt9kI ● Official Recoil docs (be aware of “This API is currently under development and will change”) https://recoiljs.org/docs/introduction/core-concepts ● Recoil source https://github.com/facebookexperimental/Recoil ● React concurrent mode https://pl.reactjs.org/docs/concurrent-mode-intro.html ● This presentation TODO PASTE LINK ;)
  • 33.
  • 34.
    Thank you! Mateusz Bryła TeamLead @ mateusz.bryla@lingmates.com Web lingmates.com LinkedIn mateusz-bryla