Users sees one part of deeply-nested state, should visible properties be at top level? - redux

I'm working on a game. Originally, the user was in a single dungeon, with properties:
// state
{
health: 95,
creatures: [ {}, {} ],
bigBoss: {},
lightIsOn: true,
goldReward: 54,
// .. you get the idea
}
Now there are many kingdoms, and many dungeons, and we may want to fetch this data asynchronously.
Is it better to represent that deeply-nested structure in the user's state, effectively caching all the other possible dungeons when they are loaded, and every time we want to update a property (e.g. action TURN_ON_LIGHT) we need to find exactly which dungeons we're talking about, or to update the top-level properties every time we move to a new dungeon?
The state below shows nesting. Most of the information is irrelevant to my presentational objects and actions, they only care about the one dungeon the user is currently in.
// state with nesting
{
health: 95,
kingdom: 0,
dungeon: 1,
kingdoms: [
{
dungeons: [
{
creatures: [ {}, {} ],
bigBoss: {},
lightIsOn: true,
goldReward: 54
}
{
creatures: [ {}, {}, {} ],
bigBoss: {},
lightIsOn: false,
goldReward: 79
}
{
//...
}
]
}
{
// ...
}
]
}
One of the things that's holding me back is that all the clean reducers, which previously could just take an action like TURN_ON_LIGHT and update the top-level property lightIsOn, allowing for very straight-forward reducer composition, now have to reach into the state and update the correct property depending on the kingdom and dungeon that we are currently in. Is there a nice way of composing the reducers that would keep this clean?

The recommended approach for dealing with nested or relational data in Redux is to normalize it, similar to how you would structure a database. Use objects with IDs as keys and the items as values to allow direct lookup by IDs, use arrays of IDs to indicate ordering, and any other part of your state that needs to reference an item should just store the ID, not the item itself. This keeps your state flatter and makes it more straightforward to update a given item.
As part of this, you can use multiple levels of connected components in your UI. One typical technique with Redux is to have a connected parent component that retrieves the IDs of multiple items, and renders <SomeConnectedChild itemID={itemID} /> for each ID. That connected child would then look up its own data using that ID, and pass the data to any presentational children below it. Actions dispatched from that subtree would reference the item's ID, and the reducers would be able to update the correct normalized item entry based on that.
The Redux FAQ has further discussion on this topic: http://redux.js.org/docs/FAQ.html#organizing-state-nested-data . Some of the articles on Redux performance at https://github.com/markerikson/react-redux-links/blob/master/react-performance.md#redux-performance describe the "pass an ID" approach, and https://medium.com/#adamrackis/querying-a-redux-store-37db8c7f3b0f is a good reference as well. Finally, I just gave an example of what a normalized state might look like over at https://github.com/reactjs/redux/issues/1824#issuecomment-228609501.
edit:
As a follow-up, I recently added a new section to the Redux docs, on the topic of "Structuring Reducers". In particular, this section includes chapters on "Normalizing State Shape" and "Updating Normalized Data".

Related

Dependency between branches in state tree in Redux

I have a state object with the following branches (trying to adhere to "Normalizing the state shape"):
Users
An array of elements like
{
id: 1,
name: "Werner"
}
originating from some server.
User locations
An array of elements like
{
userId: 1,
latitude: 45,
longitude: 70
}
originating from some server.
The problem
The users might change depending on a number of actions: SET_USERS_ACTION, ADD_USER_ACTION, DELETE_USER_ACTION.
Every time something happens to the users, I want to update the user locations (which is an asynchronous operation, as the data needs to come from the server). The how of the matter is what I'm struggling with.
Obviously, I can't fetch the user locations in the reducer (when updating the users), as the reducer would no longer be pure in that case.
I might do it in the thunk, but that would mean I have to add user location considerations to every action creator involving user-actions, which seems like mixing concerns to me.
Additionally, once an action is added that changes the users array in some way, the developer needs the remember to also update the user locations. My experience is that stuff like this will almost always be forgotten at some point.
Further complications
To further complicate the matter, we don't always need to fetch the locations. Only if a component displaying a map with all users is active, does it make sense to fetch the user locations. Not every action is generated at a place where I know (beforehand) if that component is visible or not. One example is when we receive a notification from the server (with Web Sockets) that a user was added or removed.
What is the best way of solving this problem?
I'll suggest to use https://github.com/kolodny/immutability-helper The benefit of using the update helper is that you are able to do many changes at once without touching the state many times. For example:
import update from 'immutability-helper';
...
case SET_USERS_ACTION:
return update(
state,
{
users: {
[idx]: { status: { $set: 'ready' }}
},
locations: {
$push: [{...}]
}
}
);
break;

How to organize Redux state for reusable components?

TL;DR: In case of a reusable component which has some complicated logic for managing its own state (think: a facebook comment textarea with autocompleter, emoji etc) how does one use store, actions and reducers to manage the state of multiple instances of this component spread across whole website?
Consider the real-world example from the official redux repo.
In it we have:
a RepoPage, which displays list of users who have starred a particular repo,
a UserPage, which displays a list of repos which are starred by particular user
a List, which is generic enough that it can display list of users or repos, provided the items and way to renderItem. In particular RepoPage uses User component to display each of users who starred the repo, and UserPage uses a Repo component to display each of starred repos.
Assume that I really want all of the state to be in Redux.
In particular, I want the state of every List on every RepoPage and UserPage to be managed by Redux. This is already taken care of in the example, by a clever three-level deep tree:
at the top level the key says what kind of component data is it (in the example it is called store.pagination)
then there is a branch for each particular type of context in which the component can be (store.pagination.starredByUser, store.pagination. stargazersByRepo)
then there are as many keys as there are unique contexts (store.pagination.starredByUser[login], store.pagination. stargazersByRepo[repo])
I feel that these three levels correspond also to: component type, parent type, parent id.
But, I don't know how to extend this idea, to handle the case in which the List component itself had many children, with a state worth tracking in Redux.
In particular, I want to know how to implement a solution in which:
User component remains intact
Repo component has a button which toggles its background color
the state of each Repo component is managed by Redux
(I'm happy to use some extensions to Redux, which still use reducers, but don't want to go with "just keep it in React local state", for the purpose of this question)
My research so far:
it looks like in Elm the Actions (messages) are algebraic data types which can be nested in such a way, that a parent component can unpack an "outer envelope" of the message and deliver a inner action intended for child to the child reducer (updater).
since it is a convention in Redux to use a string as the type of action, a natural translation of the above idea is to use prefixing, and this seems to be what prism (foremly known as redux-elm) does: the action.type is comprised of substrings which tell the path through components' tree. OTOH in this comment the prism author tomkis explains that the most important part of Elm Architecture that Redux is missing is composition of actions
the two above approaches seem to be expanded versions of approaches described in Reusing Reducer Logic
I haven't fully grasped how redux-fly works internally, but it seems to use the payload, not the action.type to identify a component instance by its mounting path in the store which also corresponds to a path in the components tree because of the way it is constructed manually by components
WinAPI, which to me seems quite similar to Redux if you squint, uses unique hWnd identifier for each control, which makes it super easy to check if action was intended for you, and decide where should be your state in the store.
The above idea could probably lead to something described in Documentation suggestion/discussion: Reusing Reducer Logic where each type of component has its own flat subtree indexed by unique id.
Another idea descibed in the linked thread linked above is to write a reducer for a particular type of component once, and then let the reducer for the parent component call it (which also means, that the parent is reponsible to decide where in the store the state of the child is located - again, that seems similar to Elm Architecture to me)
A very interesting discussion More on reusability for custom components in which details of a proposal vary similar to the one above is presented
in particular above discussion contains a proposition by user nav, to organize the store tree recursively in such a way, that a state of a component is subtree in two kinds of branches: one for private stuff, and the other for "tables" of child components, where each class of child component has its own "table", and each instance of child has a unique key in that table, where its state is recursively stored. The unique keys which give access to these children are stored in the "private" section. This is really similar to how I imagine WinAPI :)
another elm-inspired proposition by user sompylasar from the same thread is to use actions which contain actions for children as a payload in a "matrioshka" style, which in my opinion mimick how algebraic types constructors are nested in Elm
redux-subspace was recommended in discussion about Global Actions for prism, as a library which is both Elm-inspired and lets you have global actions.
I will try to explain one of idea which is inspired by Elm lang and has been ported to Typescript:
Let's say we have very simple component with the following state
interface ComponentState {
text: string
}
Component can be reduced with the following 2 actions.
interface SetAction {
type: 'SET_VALUE', payload: string
}
interface ResetAction {
type: 'RESET_VALUE'
}
Type union for those 2 actions (Please look at Discriminated Unions of Typescript):
type ComponentAction = SetAction | ResetAction;
Reducer for this should have thw following signature:
function componentReducer(state: ComponentState, action: ComponentAction): ComponentState {
// code
}
Now to "embed" this simple component in a larger component we need to encapsulate data model in parent component:
interface ParentComponentState {
instance1: ComponentState,
instance2: ComponentState,
}
Because action types in redux need to be globally unique we cannot dispatch single actions for Component instances, because it will be handled by both instances. One of the ideas is to wrap actions of single components into parent action with the following technique:
interface Instance1ParentAction {
type: 'INSTNACE_1_PARENT',
payload: ComponentAction,
}
interface Instance2ParentAction {
type: 'INSTNACE_2_PARENT',
payload: ComponentAction,
}
Parent action union will have the following signature:
type ParentComponentAction = Instance1ParentAction | Instance2ParentAction;
And the most important thing of this technique - parent reducer:
function parentComponentReducer(state: ParentComponentState, action: ParentComponentAction): ParentComponentState {
switch (action.type) {
case 'INSTNACE_1_PARENT':
return {
...state,
// using component reducer
instance1: componentReducer(state.instance1, action.payload),
};
//
}
}
Using Discriminated Unions additionally gives type safety for parent and child reducers.

Redux: Generic update action

I'm working with Redux and my state is a normalized one with a lot of different models. Now I was wondering myself if it was better to have specific actions like:
{type: CHANGE_MODEL_NAME, modelId, name}
vs
{type: UPDATE_MODEL, modelId, {name}}
I did a bit of searching and I found this question:
Is it ok to create generic redux update action
Now what I'm wondering is that no one is adressing the fact that having specific action types allow for different reducers to 'react' to an action in a cleaner way.
IE: I have a model that is copied from another model like so:
{
name: 'foo',
originalModel: id_0
}
It then becomes easier to react to specific actions in my reducer of copied models if I only want to react to the name change action.
Is it wrong for 2 reducers to react to the same actions? Is that why nobody adressed this issue in the original question?
Having multiple slice reducers respond to the same actions is absolutely an intended use case for Redux. I covered that background in my post The Tao of Redux, Part 1 - Implementation and Intent.
As for your specific question: I think it's entirely valid to have an update action for normalized data that contains the item type name and the item ID. In fact, I demonstrated this exact approach in my post Practical Redux, Part 7: Form Change Handling, Data Editing, and Feature Reducers.
Overall, Redux itself doesn't care what specific action types you have and how generic they are. You are encouraged to define whatever actions are appropriate for your app, and what level of "abstraction" they represent. It's very reasonable to make them a bit more generic - for example, I'd prefer UPDATE_USER_ATTRIBUTES instead of SET_USER_FIRST_NAME and SET_USER_LAST_NAME, but ultimately it's up to you.
This is perfectly valid. This pattern even has a name. "Applying a change set"
Your message becomes the following:
{type: APPLY_CHANGSET, data: {id: idOfThingToApplyTo, propOne: '1', propTwo: '2'}}
Your reducers can then look like this:
const propOneReducer = (value = 'default', {type, {data: {propOne}}) => {
return type === APPLY_CHANGSET && propOne !== undefined ? propOne : value;
}
This makes it a lot easier to add new properties (attributes) to your objects in your store. Adding a reducer, and sending the data from your react views to the actionCreator. In simple cases, you might not even need to change the actionCreator.
In these simple cases you can even build a reducer creator, basically creating the reducer for you.
Pro's
Less actions in the system
Simple sweet actionCreators
Not Pro's
Actions don't describe exactly what is happening. It's also harder to parse exactly what happens to the store after a actionCreator is invoked. This because the reducers now take the shape of the data into account.
Slightly more complex reducers

Redux: few lists with separate data managed by the same reducer

So, my application has two (or more) lists of articles that are both managed by the ArticlesReducer. And I have two associated actions:
LOAD_FAVOURITES
LOAD_FEED
And the state is:
State = {
feed: ArticleListState
favourites: ArticleListState
}
So, basically those two sub-states should have completely separate data (retrieved from the REST api) but handled it in the same manner. The only difference is what data is being managed.
ArticleListState has various associated actions like ADD_ITEM, REMOVE_ITEM, UPDATE_ITEM.
I decided to make a wrapper around the Action that also has a value responsible for identifying which sub-state should be updated.
Like so:
RoutedAction
{
action: Action,
route: string
}
And my parent reducer is now checking the route property of the RoutedAction and passes action to the appropriate sub-reducer which handles it as usual.
Can it be considered a good practice? If not, how would such use-case be normally implemented?

In firebase, is modeling many-many relationships using a separate endpoint a good idea?

Suppose I have a typical users & groups data model where a user can be in many groups and a group can have many users. It seems to me that the firebase docs recommend that I model my data by replicating user ids inside groups and group ids inside users like this:
{
"usergroups": {
"bob": {
"groups": {
"one": true,
"two": true
}
},
"fred": {
"groups": {
"one": true
}
}
},
"groupusers": {
"one": {
"users": {
"bob": true,
"fred": true
}
},
"two": {
"users": {
"bob": true
}
}
}
}
In order to maintain this structure, whenever my app updates one side of the relationship (e.g., adds a user to a group), it also needs to update the other side of the relationship (e.g., add the group to the user).
I'm concerned that eventually someone's computer will crash in the middle of an update or something else will go wrong and the two sides of the relationship will get out of sync. Ideally I'd like to put the updates inside a transaction so that either both sides get updated or neither side does, but as far as I can tell I can't do that with the current transaction support in firebase.
Another approach would be to use the upcoming firebase triggers to update the other side of the relationship, but triggers are not available yet and it seems like a pretty heavyweight solution to post a message to an external server just to have that server keep redundant data up to date.
So I'm thinking about another approach where the many-many user-group memberships are stored as a separate endpoint:
{
"memberships": {
"id1": {
"user": "bob",
"group": "one"
},
"id2": {
"user": "bob",
"group": "two"
},
"id3": {
"user": "fred",
"group": "one"
}
}
}
I can add indexes on "user" and "group", and issue firebase queries ".orderByChild("user").equalTo(...)" and ".orderByChild("group").equalTo(...)" to determine the groups for a particular user and the users for a particular group respectively.
What are the downsides to this approach? We no longer have to maintain redundant data, so why is this not the recommended approach? Is it significantly slower than the recommended replicate-the-data approach?
In the design you propose you'd always need to access three locations to show a user and her groups:
the users child to determine the properties of the user
the memberships to determine what groups she's a member of
the groups child to determine the properties of the group
In the denormalized example from the documentation, your code would only need to access #1 and #3, since the membership information is embedded into both users and groups.
If you denormalize one step further, you'd end up storing all relevant group information for each user and all relevant user information for each group. With such a data structure, you'd only need to read a single location to show all information for a group or a user.
Redundancy is not necessarily a bad thing in a NoSQL database, indeed precisely because it speeds things up.
For the moment I would go with a secondary process that periodically scans the data and reconciles any irregular data it finds. Of course that also means that regular client code needs to be robust enough to handle such irregular data (e.g. a group that points to a user, where that user's record doesn't point to the group).
Alternatively you could set up some advanced .validate rules that ensure the two sides are always in sync. I've just always found that takes more time to implement, so never bothered.
You might also want to read this answer: Firebase data structure and url

Resources