I'm in a project where we're currently using Redux for state management in our React-based Single page application, and we've run into an issue regarding when/how to clean out unused data from our stores (technically sub-state on or global Redux store).
For example we have a calendar "store" which looks
calendar = {
"2015-11-06": {
// Loads of data
},
... // More dates
}
Mostly we only care about a single date at the time, but there are cases where there are different components that needs different calendar date at the same time.
The question is: Is there some kind of strategy to "garbage collect" stores?
My initial thought is that components that need a specific calendar date will have to "reserve" that date and when it's unmounted, it'll remove its reservation. That way, when we reach some kind of size limit we can just remove all date that aren't reserved by any component.
It's a bit of a hassle though since it adds the need for components to handle "reservations" when fetching a date and when the component unmounts.
Is this a feasible strategy or is there a better alternative?
Sounds like a good use-case for a WeakSet or a WeakMap.
WeakMap holds references to its keys weakly, meaning that if there are
no other references to one of its keys, the object is subject to
garbage collection.
The key to all of this lies in a combination of this statement:
Mostly we only care about a single date at the time, but there are cases where there are different components that needs different calendar date at the same time.
...and how we think about state in an architecture like Flux/Redux.
There's nothing stopping you from restructuring your existing data store like so:
calendar = {
mainDate: {
date: "2015-11-06",
// Loads of data
}
}
Then, whenever you hit one of those special cases where you need more than one date, you issue an action that replaces the calendar state with something that looks like this:
calendar = {
mainDate: {
date: "2015-11-06",
// Loads of data
},
otherDate: {
date: "2016-02-29",
// Other data. Perhaps even less than the loads you'd have in mainDate
}
}
Somewhere along the line, your components will decide for themselves whether they need to look at mainDate or otherDate. (They may very well extract the appropriate one and then pass the contents down to their child components; you may want to introduce an abstraction layer here.)
And when the other component is done using the other date, it'll issue another action that generates:
calendar = {
mainDate: {
date: "2015-11-06",
// Loads of data
}
}
...thus automatically taking care of your garbage collection concern.
Obviously, there's a lot of implementation detail to be worked out here, but that's specific to your situation. The key concept is to contain all the state (and only the state) that you need to run the app at any given time, and use actions to make the transition from one state to another.
Related
I have a app in which users can build a "Quote". So far I've been adhering to "idiomatic" redux (or something close to it) and it's been working out well.
However, I'm struggling to deal with a fairly simple scenario:
When the page is first opened, I fire an async LOAD event which retrieves info from the server needed to build the quote (products, inventory, previously saved line items, etc).
I need some way to be able to automatically add a specific line item(s) to the quote first it's first opened.
I've added a defaultLineItems property to my LOAD payload, but to fire the addLineItem(product, inventory, options) action, I need data from the productReducer, inventoryReducer, optionsReducer. The lineItemReducer could look at these defaultLineItems and try to set it's state appropriately, but that would require having to rewrite a lot of BL typically handled by the actions using data aggregated from reducer memorized "selectors" (EG: defaulting price, quantity, currency translation, etc)
I can think of a couple ways to achieve this, but they all seem somewhat hack-ish (IE storing a flag in the reducer that says I need to fire an action and then running it when my root component props update). It seems like a react component should not be responsible for this type thing.
What is the proper way to do something like this?
Seems there are a couple different ways this can be accomplished but for me the most balanced approach between simplicity and design was to use store.subscribe in conjunction with a reducer to track the last action(s).
At it's simplest, this would look something like this.
store.subscribe(function() {
let state = store.getState();
if(state.lastAction.type === ActionKeys.LOAD){
console.log('load action fired!');
}
})
Please be aware that firing an action from store.subscribe will cause recursion so you need to be selective.
First off, I'm still a relative newbie to the world of React & Redux. I'm reading about Normalizing State Shape, and their examples are about storing data by ID. But what if I have data that is keyed on multiple dimensions?
For example, my app is displaying cost data for a given Service ID, which is retrieved from an API. However, the user is able to select a time range. The start and end timestamps are passed to the API, and the API returns the aggregated data over that time range. I want to be able to store all the different time period data in Redux so if the user goes back to a previous time period, that data is already there (our API is slow, so having already loaded data available is critical to user experience).
So not only do I have data keyed by Service ID, but also by Start Time / End Time. Since Redux recommends flat data structures, I wonder how flat should I make it? Because normally, I would store the data like this:
{
costData: {
[service_id]: {
[start_time]: {
[end_time]: {
/* data */
}
}
}
}
}
But that seems to go about the idea of flattening the data. One of my ideas was to generate an ID based on Service ID & Start Time & End Time of the form:
<ServiceID>::<StartTime>::<EndTime>
eg.
00123::1505423419::1505785502
So the data is fairly flat:
{
costData: {
'00123::1505423419::1505785502': {
/* data */
}
}
}
The component can generate this ID and pass it to the fetchCostData() action, which can dispatch and fetch the data and store that data on that generated ID. But I don't know if that's the best approach. Are there any guidelines or recommendations on how to approach this?
I recommend using selectors (Reselect) for this nested data, if you're not comfortable with modifying your api.
-> Selectors are the best approach for computing derived data, allowing Redux to store the minimal possible state.
-> Selectors are efficient. A selector is not recomputed unless one of its arguments change.
-> Selectors are composable. They can be used as input to other selectors.
In addition to the other answer, you may want to read the article Advanced Redux Entity Normalization, which describes ways to track additional lookup descriptions of normalized data. I also have some other articles on normalization in the Redux Techniques#Selectors and Normalization section of my React/Redux links list.
I'm using the ultimate combination of React + Redux + Reselect + Immutable.js in my application. I like the idea of reselect because it lets me keep my state (maintained by the reducers) as simple as possible. I use a selector to calculate the actual state I need which is then fed to the React components.
The problem here is that a small change in once of the reducers causes the selectors to recalculate the whole derived output and as the result also the whole React UI is updated. My pure components don't work. It's slow.
Typical example: The first part of my data comes from server and is basically immutable. The second part is maintained by the client and is mutated using the redux actions. They are maintained by separate reducers.
I use a selector to merge both parts into a single list of Records which is then passed to the React components. But obviously, when I change a single thing in one of the objects, the whole list is regenerated and new instances of Records is created. And the UI is completely re-rendered.
Obviously running the selector every time is not exactly efficient but is still reasonably fast and I'd be willing to make that trade off (because it does make the code way simpler and cleaner). The problem is the actual rendering which is slow.
What I'd need to do would be to deep merge the new selector output with the old one because Immutable.js library is smart enough not to create new instances when nothing was changed. But as selectors are simple functions that do not have access to previous outputs, I guess it's not possible.
I assume that my current approach is wrong and I'd like to hear other ideas.
Probably the way to go would be to get rid of reselect in this case and move the logic into a hierarchy of reducers that would use incremental updates to maintain the desired state.
I solved my problem but I guess there is no right answer as it really depends on a specific situation. In my case, I decided to go with this approach:
One of the challenges that the original selector handled nicely was that the final information was compiled from many pieces that were delivered in an arbitrary order. If I decided to build up the final information in my reducers incrementally, I'd have to make sure to count with all possible scenarios (all possible orders in which the information pieces could arrive) and define transformations between all possible states. Whereas with reselect, I can simply take what I currently have and make something out of it.
To keep this functionality, I decided to move the selector logic into a wrapping parent reducer.
Okay, let's say that I have three reducers, A, B and C, and corresponding selectors. Each handles one piece of information. The piece could be loaded from server or it could originate from the user on the client side. This would be my original selector:
const makeFinalState(a, b, c) => (new List(a)).map(item =>
new MyRecord({ ...item, ...(b[item.id] || {}), ...(c[item.id] || {}) });
export const finalSelector = createSelector(
[selectorA, selectorB, selectorC],
(a, b, c) => makeFinalState(a, b, c,));
(This is not the actual code but I hope it makes sense. Note that regardless of the order in which the contents of individual reducers become available, the selector will eventually generate the correct output.)
I hope my problem is clear now. In case the content of any of those reducers changes, the selector is recalculated from scratch, generating completely new instances of all records which eventually results in complete re-renders of React components.
My current solution looks lite this:
export default function finalReducer(state = new Map(), action) {
state = state
.update('a', a => aReducer(a, action))
.update('b', b => bReducer(b, action))
.update('c', c => cReducer(c, action));
switch (action.type) {
case HEAVY_ACTION_AFFECTING_A:
case HEAVY_ACTION_AFFECTING_B:
case HEAVY_ACTION_AFFECTING_C:
return state.update('final', final => (final || new List()).mergeDeep(
makeFinalState(state.get('a'), state.get('b'), state.get('c')));
case LIGHT_ACTION_AFFECTING_C:
const update = makeSmallIncrementalUpdate(state, action.payload);
return state.update('final', final => (final || new List()).mergeDeep(update))
}
}
export const finalSelector = state => state.final;
The core idea is this:
If something big happens (i.e. I get a huge chunk of data from the server), I rebuild the whole derived state.
If something small happens (i.e. users selects an item), I just make a quick incremental change, both in the original reducer and in the wrapping parent reducer (there is a certain duplicity, but it's necessary to achieve both consistency and good performance).
The main difference from the selector version is that I always merge the new state with the old one. The Immutable.js library is smart enough not to replace the old Record instances with the new Record instances if their content is completely the same. Therefore the original instances are kept and as a result corresponding pure components are not re-rendered.
Obviously, the deep merge is a costly operation so this won't work for really large data sets. But the truth is that this kind of operations is still fast compared to React re-renders and DOM operations. So this approach can be a nice compromise between performance and code readability/conciseness.
Final note: If it wasn't for those light actions handled separately, this approach would be essentially equivalent to replacing shallowEqual with deepEqual inside shouldComponentUpdate method of pure components.
This kind of scenario can often be solved by refactoring how the UI is connected to the state. Let's say you have a component displaying a list of items: instead of connecting it to the already built list of items, you could connect it to a simple list of ids, and connect each individual item to its record by id. This way, when a record changes, the list of ids itself doesn't change and only the corresponding connected component is re-rendered.
If in your case, if the record is assembled from different parts of the state, the selector yielding individual records could itself be connected to the relevant parts of the state for this particular record.
Now, about the use of immutable.js with reselect: this combination works best if the raw parts of your state are already immutable.js objects. This way you can take advantage of the fact that they use persistent data structures, and the default memoization function from reselect works best. You can always override this memoization function, but feeling that a selector should access its previous return value if often a sign that it is in charge of data that should be hold in the state / or that it is gathering too much data at once, and that maybe more granular selectors could help.
It looks like you are describing a scenario very close to the one why I wrote re-reselect.
re-reselect is a small reselect wrapper, which initializes selectors on the fly using a memoized factory.
(Disclaimer: I'm the author of re-reselect).
TL;DR edit in retrospect years later: there's no solution that's not gross as long as it's just state data - you'll need to also get it into a separate store somewhere somehow and can do whatever you want at that point. But read the question and the answer and the back-and-forth if you want some more background.
I have a table of two sections, each with various input values. Let's say that it is a survey. Feeding data into this is straightforward; I have the typical model:
{ "sections": [ { "name": "a", values: { "A": 1, "B": 2, "C": 1, ... } }, ... ], ... }
And a component hierarchy like:
<Survey>
<Section> (for each section)
<ValueRow> (for each value)
I put the model into a prop on the survey and the right information is trickled down into the subcomponents. Each ValueRow has a text field and its ephemeral value reflected back into its own state. This works fine "on the way down", in the one way flow that React is built for.
However, I also wish to show progress on the Section level and for the entire Survey, both simple things like number of fields filled out and statistical data needing the entire data set - what's the average across sections, how many "1" answers do I have in total, what's my grade (calculated from all the answers) and so on. Essentially, I'd also want to have:
<Survey>
<SurveyWideStats>
<Section> (for each section)
<SectionWideStats>
<ValueRow> (for each value)
This turns into a reduction of the current state instead of the model data. What's the best way of doing this in React? Flux and Actions and Stores all seem to deal with how to handle the data once it has been committed to the model. What I want to do is to pluck all the state data and do something with it, but it also seems terribly gross for the SurveyWideStats element, for example, to go poking through the garbagestate of its sibling element's children.
My current solution is to pass around an accumulation object and provide enough state to each component that it can keep calling that whenever something changes. This seems clear and divided enough, but it means that I have to have two passes and have to be careful not to start fiddling with state during rendering (at least since that's when I call the accumulation object - I suppose there may be a better point during the lifecycle where I could call that). And in addition, it seems like this would be an obstacle to "pick up from" server side rendering.
What's the best way? Is there an established pattern for this - preferably one where these things don't have to be so custom and really tailored to the data all the time?
Two ways to do this:
Pass the entire table as a prop to the highest component .
Inside survey's render function, calculate the stats, then pass them to the component as props, followed by the foreach loops over the table for the other children components. That way, your stats component is a pure component, does not need state and does not need to poke in siblings.
Create a stats function in a store, and have the component call this to get the stats. NB best not to save the stats in a store, since it is clearly derived data. Unless for performance reasons.
Hope this helps!
UPDATE:
To handle changes by the user when they change an input value, you have two options, depending on your preference:
(Option 1 describes a pure component).
(When you use flux pattern): Put the value of the input control in props. And whenever the user makes a change, fire an action to update a store, and have the store pass down updated props. So the (top) component notices a change event and rerenders. This creates more or less 'live' updates, e.g. when a user types a single character in an input field, the page title is updated immediately. The component with the input control does not have (and does not need) setState. This setup may become slow in really large component trees (because with each character, the entire tree is rerendered). But react is superfast and smartly only renders changes in de tree.
Put the initial prop value in state (in getInitialState() and put the input value in state also. Typical example: user types a character in an input field, the change triggers a setState() and the component is rendered again. Only when the user clicks some save or commit button, an action is fired to save the value in a store.
UPDATE:
As a bonus, below the flow for updating stores and components.
I am working on a scheduling application using Reactjs & Flux. In the application, a user will need to be able to select a specific shift and then assign an employee to cover it. Employee assignment will occur by clicking on an employee's name in a separate list.
Component structure is as follows:
Schedule App Component
Employee List Component
Employee List Item Component
Calendar Component
Month Component
Day Component
Shift Component
After selecting a shift by clicking on it, (there are 2 - 5 shifts on any given day) I would like to be able to click on an Employee List Item and have the value of its employeeName prop (this.props.employeeName) assigned to the selected Shift's shiftAssignee prop (this.props.shiftAssignee).
The data for both the calendar and the employees are all generated when the application starts, and stored as separate objects in local storage as 'calendar' and 'employees' respectively. Ultimately I would like to have this data updated as as part of the flux data flow so that I can retain it instead of regenerating it every time the app starts, wiping all the previous data, but that's not my most immediate concern.
The basic structure of that data looks pretty much as follows:
Calendar:
{
"MonthName": "May",
"Days": [
{
"DayDate": 1,
"DayName": "Friday",
"Shifts": [
{
"shiftName": "Day Shift",
"required": true,
"shiftAssignee": "",
"shiftLength": 12
}
//remaining shifts
],
}
//remaining days
]
}
Employees:
[
{
"name": "F. Last",
"totalHours": 32,
"availableHours": 32,
"assignments": [],
"commitment": 0.8
}
//remaining employees
]
I don't know if this is more information than is needed or if I'm overlooking something that would be crucial for consideration. If more information is needed, just let me know.
I don't believe you need to be concerned with the relationship between the two child components.
In a flux application, the data flows in to the topmost component and is passed down to the children. Looking at it from this way, I believe your question can be rephrased as: "How do I get a child component to change the data from the flux store?"
I've written up a very rough example in a codepen: http://codepen.io/jhubert/pen/ZGJEdp
It's a very lightweight conceptual version of a flux store / dispatcher built into one. I don't recommend copying the example; It's the concept we're after.
Essentially, you want to your Employee List Item Component to modify the data and then let the natural cascading data flow work from there.
# not real code. kind of like coffeescript but really not real.
_data = { { { shiftAssignee: '' } } }
ScheduleAppComponent.setState = _data
EmployeeListItem.onClick = setShiftAssigneeOnData()
ScheduleAppComponent.setState = _data
This stuff is mostly covered at a high level in the flux overview under Structure and Data Flow.
Hope that's helpful! Good luck!
Your issue seems to be your usage(or lack) of state. I think you might have to extract some of the details to state, so that when it is updated anywhere, it cascades to other parts of the view. Something like
"shiftAssignee" in your calendar object seems to be a good parameter to watch as state.
I see you tagged this with reactjs-flux, but it doesn't seem you've thought about implementing Flux stores and actions much so far. Flux is a pattern that could help this data flow happen, but if you're interested in Flux, you kind of need to go all the way and implement stores, actions, and event listeners.
With a Flux implementation of this app, both components should listen to a store event about employee data changing, which could fire whenever an employee is assigned to a shift. Then they can fetch the new data and update their display (to show the shift filled and to show the employee busy during that block/update their scheduled hours total). A different event should be triggered by the selection of an empty shift (an action is created and dispatched, active shift is noted if necessary in the calendar store, an event is emitted to notify the components that a certain shift is ready to be filled), the components can update themselves (maybe asking the store which shift is now active).
It's perfectly natural in a Flux app to have this kind of interdependence and interactivity between different React components, as it's pretty much the case that Flux was invented to solve. A non-Flux approach could also work, but you likely would want to wrap both components in a mutual parent that handles the interactions for them, passing down functions to the components that do the work.