DEV Community

Atle Frenvik Sveen
Atle Frenvik Sveen

Posted on

Recoil atom effects

..are effing cool!

I've been using Recoil.js for a while now, but I've never taken the time to dive into atom effects before recently.

Why did I do so? Because I needed a timer. Or a clock if you will. Running in recoil. And atom effects seems to do the trick. Just look here

import {AtomEffect, atomFamily} from 'recoil';

export type Millisecounds = number;

const getUnixNow = () => Math.floor(Date.now() / 1000);

const clockEffect =
  (interval: Millisecounds): AtomEffect<number> =>
  ({setSelf, trigger}) => {
    if (trigger === 'get') {
      setSelf(getUnixNow());
    }
    const timer = setInterval(() => setSelf(getUnixNow()), interval);
    return () => clearInterval(timer);
  };

/**
 * Atom that contains the current unix timestamp
 * Updates at the provided interval
 */
export const clockState = atomFamily<number, Millisecounds>({
  key: 'clockState',
  default: getUnixNow(),
  effects: (interval: Millisecounds) => [clockEffect(interval)],
});
Enter fullscreen mode Exit fullscreen mode

This gives you an atomFamiliy that can be instantiated with the desired interval, and this atom automagically updates each interval, in this case returning the current unix timestamp

const time = useRecoilValue(clockState(1000)); //new clock state that updates every secound

return <div>The current Unix time is now: {time}</div>
Enter fullscreen mode Exit fullscreen mode

Neat?

Well. But what you can do is use this as a trigger in a selector that needs to run periodically

export const pollerState = selector<SomeData[]>({
  key: 'pollerState ',
  get: async ({get}) => {
    //add this to referesh every minute
    get(clockState(60000));
    return await myApi.getSomeData();
  },
});
Enter fullscreen mode Exit fullscreen mode

And this is pretty neat!

And if this doesn't get you hooked on atom effects, take a look at this, straight outta the recoil docs (Just some TypeScript added):

export const localStorageEffect =
  <T>(key: string): AtomEffect<T> =>
  ({setSelf, onSet}) => {
    const savedValue = localStorage.getItem(key);
    if (savedValue != null) {
      setSelf(JSON.parse(savedValue));
    }
    onSet((newValue, _, isReset) => {
      isReset ? localStorage.removeItem(key) : localStorage.setItem(key, JSON.stringify(newValue));
    });
  };

const syncedState = atom<string>({
  key: 'syncedState',
  default: '',
  effects: [localStorageEffect('local_storage_key')],
});
Enter fullscreen mode Exit fullscreen mode

This actually syncs your atom to local storage. Sweet!

Top comments (0)