How to best store a complex object of MQTT messages in a React App State?

Viewed 345

I want to develop a React App with TypeScript, which can store and send MQTT data.

There are a lot of (hundreds) MQTT messages like: config.laser.enable == "true", config.system.value == "23" or config.user.password == "foobar".

So I end up with an object like:

{
   "config.laser.enable": true,
   "config.system.value": 23,
   "config.user.password": "foobar",
   ...
}

Each of my visual components will use only a small subset of these topics.

My first approach was to create a MQTT client for each component and subscribe to their individual topics, but this seems to be a lot of overhead. So I believe it would be better to have only one MQTT client, which subscribes to everything and stores the message data in a global state. So my question is: How can I (or shouldn't I?) store complex data (an object) in a global state in React and making sure that only the components redraw when their specific subset of the global state has changed. Can I use React Redux for this?

Can you give me an example on how to do this?

I am new to React, so please forgive if this is trivial.

1 Answers

React context should be a good fit for such tasks. It is also the backbone for the Redux library. Here is an excerpt from the React manual:

Context is designed to share data that can be considered “global” for a tree of React components

A proof-of-concept example of using React context:

const GlobalMessages = React.createContext({});

function App() {
  return (
    <GlobalMessages.Provider value={/* pass the messages here */}>
      <SomeComponent/>
      <RandomContainer/>
    </GlobalMessages.Provider>;
  )
}

function SomeComponent() {

  // All instances of SomeComponent nested at any level inside
  // GlobalMessages.Provider will now have access to its value.
  let msgContext = useContext(GlobalMessages);

  let message = msgContext.messageTypeA; // Your "selector"

  return <Textbox value={message} />;
}

Note that in the above example all components that use the GlobalMessages.Provider context will rerender when its value prop changes and this might have some performance implications.

Fortunately, React component rerenders are usually quite cheap due to virtual DOM. This means most renders will never touch the real DOM at all. If you are sure you have an expensive component that you would like to avoid rerendering - you can memoize it like this:

function SomeComponent() {
  let msgContext = useContext(GlobalMessages);
  let message = msgContext.messageTypeA; // Your "selector"

  return useMemo(() => {
    // The rest of your rendering logic
    return <ExpensiveComponent value={message} />;
  }, [message])
}
Related