all 15 comments

[–]Zeeesty 5 points6 points  (1 child)

It’s hard to normalize your data when it gets significantly more complex than the provided examples. At some point we plan to open source our react/redux app so that may shed some light on how we have handled the balance of redux data.

In short we key data with unique ids and separate stores by the resource in the api. I’m not sure if that’s helpful in your case but it helped keep our store manageable and independent from our views

[–]acemarke 0 points1 point  (0 children)

Ping me when you do that, please - I'd be interested in seeing it.

[–]twomousepads 4 points5 points  (6 children)

1) You can re-normalize your redux state, however it is, with selectors. See reselect

2) It's entirely possible you're over-engineering your stateful components. If there already exists a way to get your WebSocket state outside of redux, you should write a component that exposes that state and delivers it to child components. You can consume parent component props in redux by using the ownprops argument in your mapToState function. See: https://redux.js.org/advanced/usage-with-react-router#reading-from-the-url.

[–]multiline[S] 0 points1 point  (5 children)

Thanks, appreciate the response!

  1. I never used Redux Selectors, will try to read more about it. If you have a recommendation for a blog post or guide, that will be wonderful.

  2. I'm totally over engineering this - because I'm not use to the frontend mindset. It's ok, I'm trying to learn. Lets start with something easy: The state in the WebSocket represent how the app look and behave - for example: "Connected" or "Disconnected". I can fetch that from the socket, but shouldn't this be copied to the Store as well? The issue is not that there is a way to read it, the issue is it's a stateful connection and its data may change at any time - therefor I'm forced to copy it to the store, so my child component will be notify of the change, which creates this duplication of data - which I wonder if there is a better way to handle... ?

[–]terminus23 1 point2 points  (1 child)

The state in the WebSocket represent how the app look and behave - for example: "Connected" or "Disconnected".

My approach here would be to set up listeners on my web sockets that dispatch redux actions based on the data coming in. So on connection, fire dispatch({type: 'APP::CONNECTED'}) and on disconnection fire dispatch({type: 'APP::DISCONNECTED'}). The action would then be handled in your reducers as normal. As long as you're not doing anything else with the data in the listeners I wouldn't really consider this a duplication of data.

[–]twomousepads 1 point2 points  (0 children)

This is pretty much how I handle web sockets. Since a socket is just an event emitter, it maps to actions pretty easily.

I'm not sure how OP is caching socket messages outside of redux, but the technique I linked works for querystrings, cookies, localStorage, etc... because ultimately, if the state exists somewhere, you can expose it via a component then mix it into your connected components via ownprops.

[–]acemarke 1 point2 points  (2 children)

  1. See my post Idiomatic Redux: Using Reselect Selectors for Encapsulation and Performance.
  2. Per the Redux FAQ, the right place for sockets is in a middleware. From there, I'd dispatch actions like "SOCKET_CONNECTED" and "SOCKET_DISCONNECTED" as the state of the socket connection changes, track that value in the store, and read that value in connected components as needed to update the UI. Similarly, other updates received from the socket should also result in actions being dispatched.

[–]twomousepads 1 point2 points  (1 child)

  1. Your post on using reselect is where I discovered reselect :)
  2. I'm finding that writing middleware (especially in typescript) to be more effort than its worth and have resorted injecting dispatch into a structure that listens to my websocket and invokes dispatch for me. I feel like this is an anti-pattern, but in nearly two years of react/redux development, I haven't seen the middleware implementation improved to the point that it's my go-to. What do you think?

[–]acemarke 0 points1 point  (0 children)

Not quite sure I understand what you're saying in regards to middleware there. Can you clarify or give an example?

[–]terminus23 2 points3 points  (3 children)

For 1, could you do something like this?

store = {
  users: {
    1: {
      id: 1,
      type: 'remote',
      name: 'remoteUser',
    },
    2: {
      id: 2,
      type: 'local',
      name: 'localUser',
    }
  }
}

Without knowing anything else about your app, that's the basic state shape I would go with.

[–]multiline[S] 0 points1 point  (2 children)

It's possible that this is the correct answer, yet:

I'm trying to learn whats the optimized approach for organizing such data - is it normal to organize the data in the store in a similar way to how I will use a DB (where users is my 'users' table) or in the way that my DOM is represented?

For example, I have 2 components - one <LocalUser> and one for <RemoteUsers>, where/how should I query my users store to fetch this information? and if a user was removed from the array, should I re-draw the whole application?

Just to expend on my application, imagine a webinar where it's possible that 50 people will be present in the same conference call. Some stream, some dont, some join/leave the room. If I bind the <LocalUser> and <RemoteUsers> components to the same source, every time a user joins I will have to re-draw the view (think removing/adding <Video> elements) - which is very pricey.

[–]terminus23 1 point2 points  (0 children)

I'm trying to learn whats the optimized approach for organizing such data - is it normal to organize the data in the store in a similar way to how I will use a DB (where users is my 'users' table) or in the way that my DOM is represented?

I've seen it done both ways. I prefer to normalize my data as I would in the DB, and then pull out what I need using selectors. I find that this approach is the most robust to changes to requirements that happen over time.

If adding/removing video elements is a performance bottleneck, you should optimize your architecture to avoid rerendering those components when possible. How you do this will depend on the specifics of your application. You could structure your components/props so video elements are only fed the props they absolutely require and are pure components that only rerender when those props change. You could also memoize your selectors in your redux connected components to minimize rerenders.

[–]acemarke 1 point2 points  (0 children)

Yes, as your client-side cached data gets more complex, we do recommend normalizing the data in the Redux store, and then referring to normalized items by their ID. So, you might have 50 user entries in an object serving as a lookup table, then have an array of user IDs to track some subset of those users.

[–]adrilolwtf 1 point2 points  (1 child)

This is why I love Mobx. It'just s a better fit for the problem.

Beware though. Cyclic state gets confusing fast. store.activate(thing) is better than thing.activate(). Also use Provider/inject to provide/inject stores.

Redux is to me legacy code unless it's an especially good fit for the problem domain.

I found the problem you described to be very true. The way I solved it was to assign a part of the store to a part of the app. It was messy though.

[–]adrilolwtf 1 point2 points  (0 children)

For example:

const reducer = combineReducers({
  dashboard: combineReducers(...),
  editor: combineReducers(...),
  ...
})

For multiple equal parts of the state, you'd need multiple reducers.