4

What is the state of the art in React to implement an event based pattern ( Publisher / Subscriber).

From one side we have components that listen to those events. Some of these components are visual, e.g. a chart that draws the result of a query other might not be visual, e.g. if we want to have components that manage queries and results.

From the other, any component can generate events.

Our first idea is adding in Redux the full list of events and their values and on each component implement in shouldComponentUpdate() a smart code that stops if the component does not listen to the changed events.

This doesn't look very elegant as all components listen to all events. Is there a more elegant solution (kind of state of the art) ?

Of course we want to be able to go back, forwars, save state and all this small details :-)

ic3
  • 7,917
  • 14
  • 67
  • 115
  • You can do that using context. Please go though its [docs](https://reactjs.org/docs/context.html#when-to-use-context) – Shubham Khatri Feb 19 '19 at 09:12
  • That's a nice part of the solution, but I still want my components to be notified when a event changes. – ic3 Feb 19 '19 at 09:19
  • Context Consumers listen to any changes in value that are v=provided to the Context provider so yes it is build on top of publish-subscribe pattern – Shubham Khatri Feb 19 '19 at 09:21
  • Thanks, I'm reading .... – ic3 Feb 19 '19 at 09:25
  • Redux is fine for us as an alternative to react context ( https://stackoverflow.com/questions/49568073/react-context-vs-react-redux-when-should-i-use-each-one ). Take into account this is just part of the actual problem that is more complex. But still looking for something more elegant – ic3 Feb 19 '19 at 09:35
  • `Redux` is both elegant and very clean way to react to events (actions) and bundled with `redux-saga` is able to perform very complex chains of events in a predictable and debuggable way. Which other system let you to time travel in your events pipeline ? If you add also libraries as `reselect` there you can choose which properties in redux store you want to listen to. – Mosè Raguzzini Feb 19 '19 at 10:00
  • Could you update your code with a minimal example. For instance what kind of events you are taking about. Does listening to events mean listening to data change or actual events – Shubham Khatri Feb 21 '19 at 13:42
  • The events change the content of the components. You can think it's just a box with a text with the event content inside – ic3 Feb 21 '19 at 15:47
  • Do you mean for an event to be a for example shortcut event which multiple that multiple components listen to or a socket event? – Shubham Khatri Feb 21 '19 at 16:12
  • for example, or another component. – ic3 Feb 22 '19 at 07:28
  • Use RxJs to solve your problem. – Sachin Bhandari Feb 28 '19 at 05:27

2 Answers2

2

I think what you need to know is not so much "What is the state of the art" as "What is the canonical way" of doing publish and subscribe with React and Redux.

The short answer is that if you have a complex enough application, you should organize your store so that the application state is divided into slices and use the container pattern to separate the responsibility of publishing and subscribing. This way you avoid the problem you mentioned where you don't know what in the code base is changing the store and what is responding to the changes. You can look at a container component and see how it handles UI events from its children and produces a state change event. Or you can see how the container handles a state change and then drill-down the hierarchy. If you can make a Redux slice the responsibility of a single container, it's that much easier to think about events. All other components are not subscribed to the events per se, instead they receive the changes they need to render from props originating from the container component. And they notify the container component of their own events through callbacks passed down through props, so the container can publish them. This can go a long, long way, and if you feel that you need to pass down props too many levels, you can use React.children to flatten the nesting a bit, or in rare cases context.

The longer answer is a bit more difficult, since publish and subscribe is not super meaningful when talking about React. React should ultimately be responsible for rendering. As you mentioned, not all events are UI events. But if you model things such that all events that you can publish and subscribe to boil down to changing the store or reacting to a store change, then you can build your app focused more on Redux. Managing the queries and results you're talking about should be done in simple JavaScript modules without React. Then you can use a container component to glue the store to that module.

There are additional patterns people use like action creators and selectors. Those are good because at least the intent is to keep code bases familiar. But things are still moving a bit fast, with React moving towards the Hooks API and React-Redux trying to catch-up. But slices and container components aren't going anywhere, they are a natural way to separate concerns.

Ed I
  • 7,008
  • 3
  • 41
  • 50
  • thanks, I've a bit a biased way of seeing thinks. But what maybe I didn't explain is that the events I'm listening are dynamically defined in a component. E.g. there is a property with the title string field that is actually the content of an event. How can I dynamically 'plug' my component with the event (hope it's clear) – ic3 Feb 27 '19 at 08:46
  • I don't understand what you mean, but a couple of remarks: If you cannot apply the container pattern, then your domain model or requirements must be very complicated. If you need to create a custom abstraction layer to deal with these dynamic events, beware of using class components and `shouldComponentUpdate`. Read the motivation for hooks: https://reactjs.org/docs/hooks-intro.html#motivation. – Ed I Feb 27 '19 at 16:32
  • Let's say the content of a field, #title , e.g. a text inside a component that is just plain html might be the actual value of an event ( $selectedYear ). This will be 'inside' a state's field. So when the $selectedYear changes the box is updated , but I can also change the the field #title directly. Hope it's a bit more clear now – ic3 Feb 28 '19 at 08:15
1

redux to store the events reselect/connect to subscribe to them (https://github.com/reduxjs/reselect#connecting-a-selector-to-the-redux-store)

import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import TodoList from '../components/TodoList'
import { getVisibleTodos } from '../selectors'

const mapStateToProps = (state) => {
  return {
    todos: getVisibleTodos(state)
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onTodoClick: (id) => {
      dispatch(toggleTodo(id))
    }
  }
}

const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

export default VisibleTodoList

Now you have your subscribed event as a prop in the component, the component will re-render only if the prop changes. No need for shouldComponentUpdate because your component doesn't have to listen to the whole store, it will only get the part of the store you define in the selector.

Radu Luncasu
  • 975
  • 10
  • 17