Search code examples
reactjsrecoiljs

Atom dictionary structure


I receive data from a websocket in my react app. The data holds prices for stock symbols:

{symbol: "aapl", price: 150}

I want to save the price for each stock in an individual Atom, where the key is the symbol and data is the price. There could be thousands of Atoms, each corresponding to a unique stock.

Is there a way I can dynamically create a new Atom, set (or update) its data, and read the Atom state somewhere else in the app? Essentially, a dictionary of Atoms, where the key is the stock symbol and state is stock price.


EDIT: This should provide more clarity on my issue: https://github.com/facebookexperimental/Recoil/issues/1406

Context: I use a websocket to stream stock prices. The realtime data comes in bundles every 0.25 seconds, containing hundreds of stock prices in the following format: {symbol:"aapl", price:150}

Issue: I need to create an independent state for each of the stocks, updating their prices as the data comes in. It also needs to be performant, as sometimes I receive 8000 stocks per second. The stock prices will be used in components throughout the app, some of which are not connected to the parent websocket component at all.

As far as I know there are two options:

Create one big Atom containing the whole websocket message, then subscribe to this big block of state

-This would cause all subscribed components to update at once, no?

Generate an Atom for each unique stock, and update the atom when a new stock price is received

-I do not know how to do this in Recoil

Essentially it comes down to a key-value pair where the key is stock symbol and value is an Atom storing the price state. Is there a way I can create such a structure and allow independent components to subscribe to these Atoms? E.g. a component "X" subscribes to the state corresponding to stock "AAPL"... Any ideas?


Solution

  • Going to answer my own question. Better late than never.

    I defined atoms in atoms.js

    import { atomFamily } from "recoil";
    
    export const stockAtom = atomFamily({
        key: "symbol",
        default: {},
    });
    

    In my setter file (where I want to set the atom values):

    import { useRecoilTransaction_UNSTABLE } from "recoil";
    import { stockAtom } from "./atoms";
      // ... functional component
      const updateState = useRecoilTransaction_UNSTABLE(
        ({ set }) => (stockObj) => {
          for (const stock of stockObj) {
            // set the atom of stock.sym to the jsondata from the websocket
            set(stockAtom((stock.sym).toLowerCase()), stock);
          }
        },
        []
      );
    
      // websocket on message
      const _onMessage = (msg) => {
        const data = JSON.parse(msg.data)
        updateState(data)
      }
      // ...
    

    Then in a file I want to consume/get the atoms in:

    import { stockAtom } from "./atoms";
    import { useRecoilValue } from "recoil";
    // passed stock sytmbol as props string
    const WatchlistItem = (props) => {
      // consume the stock price of atom with key props.symbol
      const symbolData = useRecoilValue(stockAtom(props.symbol));
      return (<div> symbolData["price"]</div>);
    }
    

    I recommend doing error handling with objects/dicts, which I left out for this example