Imagine the following:
you're writing a 'smart-house' application which manages a temperature in your house.
In my view I'd like to:
see current temperature for each room
set desired temperature for each room
see whether air conditioning is turned on/off for each room
There is an external device, communicating with your aplication via websockets (it is periodically sending current temperature, air conditioning status).
I see two options there:
1) Create one 'big' store containging data structures like:
var _data = [
name: 'Kitchen',
currentTemperature: 25,
desiredTemperature: 22,
sensors: [
{
name: 'Air Conditioning'
state: 'on'
}
... there might be other sensors too ...
]
]
There will be a TemperatureManager component (or something similar). It would have a state and fetch it from Store periodically.
Then it would just distribute part of the state to his descendants (ie RoomTemperatureManager, RoomSystemSensorManager), passing it as props.
If anything changes (for example, temperature in the bedroom), it will fetch all data from store and re-render its descendants if necessary.
2) The second solution is
to make RoomTemperatureManagers and RoomSystemSensorManagers have their own state. It is also related to having standalone stores for both Temperature and SystemSensorState.
Those Stores would then have parametrized getters (ie getSensorState(roomName)) instead of methods to fetch all data.
Question:
Which option is better?
Additional question:
Is it okay for leaf components (ie the one responsible for managing desired temperature) to call ActionCreator directly? Or maybe only the Supervising Component should know anything about ActionCreator and should pass proper method as a property to his descendants?
The 2 options you describe in your post are really 2 diffent questions:
One big store or several different stores?
Should child components (RoomTemperatureManagers) have their own state or receive props from store?
Ad 1. One big store is easier, as long as it does not get to complicated. Rule of thumb that I use: if your store has > 300 lines of code, probably better to separate in different stores.
Ad 2. Props are generally better than state. But in your case, I would think you will need state in e.g. your Temperature-manager: you set the temperature in your app (and want to see that reflected in some slider or whatever). For this, you will need state.
State updates the front-end immediately (optimistic update).
The component then sends of a set_temparature action to a sensor in the
house.
Then the sensor in the house confirms that it has set the new
temperature to your app.
The store(s) update(s) and emit change.
The temperature setting from the sensor in the house is communicated as
props to your Temperature manager.
The Temperature manager does whatever it needs to do (simply update state with new prop, display confirmation message, or nice error message if things broke down along the communication chain).
I believe the best approach would be to delegate. Your TemperatureManager should have a mechanism to notify listeners that there is an update and in turn, the Room(Temperature|System)Manager would send as a listener a callback that would consume the updated data and change the state accordingly. This will leverage the virtual DOM diff so only changes will be displayed and not whole parts re-rendered as well as create a single point that would communicate with the Store. Room(Temperature|System)Manager should only communicate with the Store if there is an update it needs to do to the model it is working with.
TemperatureManager could be improved if on subscribing, you can specify which data to listen to for updates. It should not assume that a particular 'manager' should get any subset of data.
Related
I could a have a flux action like this:
{type: 'KILL', payload: {target: 'ogre'}}
But I am not seeing what the difference is between having a method on a class People (wrapping the store) like this,
People.kill('ogre')
IF People is the only receiver of the action?
I see that the flux dispatcher gives me two advantages (possibly)
The "kill" method can be broadcast to multiple unknown receivers (good!)
The dispatcher gives me a handy place to log all action traffic (also good!)
These might be good things sure, but is there any other reasons that I am missing?
What I don't see is how putting the actions in the form of JSON objects, suddenly enforces or helps with "1-way" communication flow, which is what I read everywhere is the big advantage of having actions, and of flux.
Looks to me like I am still effectively sending a message back to the store, no matter how I perfume the pig. Sure the action is now going through a couple of layers of indirection (action creator, dispatcher) before it gets to the store, but unless I am missing something the component that sends that action for all practical purposes is updating whatever stores are listening for the kill message.
What I am missing here?
Again I know on Stack Overflow we can't ask too general a question, so I want to keep this very specific. The two snippets of code while having different syntax, appear to be semantically (except for the possibility of broadcasting to multiple stores) exactly the same.
And again if the only reason is that it enables broadcasting and enables a single point of flow for debug purposes, I am fine with that, but would like to know if there is some other thing about flux/the dispatcher I am missing?
The major features of the flux-style architecture are roughly the following:
the store is the single source of truth for application state
only actions can trigger mutation of the store's state
store state should not be mutated directly, i.e. via assigning object values, but by creating new objects via cloning/destructuring instead
Like a diet, using this type of architecture really doesn't work if you slip and go back to the old ways intermittently.
Returning to your example. The benefit for using the action here is not broadcasting or logging aspects, but simply the fact that the People class should only be able to either consume data from a store and express its wishes to mutate the state of said store with actions. Imagine for example that Elves want to sing to the the ogre and thus are interested in knowing the said ogre is still alive. At the same time the People want to be polite and do not wish to kill the ogre while it is being serenaded. The benefits of the flux-style architecture are clear:
class People {
kill(creature) {
if (creatureStore.getSerenadedCreature() !== creature)
store.dispatch({ type: 'KILL', payload: { target: creature } })
return `The ${creature} is being serenaded by those damn elves, let's wait until they've finished.`
}
}
class Elves {
singTo(creature) {
if (!creatureStore.getCreatures().includes(creature))
return store.dispatch({ type: 'SING_TO', payload: { target: creature } })
return `Oh no, the ${creature} has been killed... I guess there will be no serenading tonight..`
}
}
If the class People were to wrap the store, you'd need the Elves class to wrap the same store as well, creating two places where the same state would be mutated in one way or the other. Now imagine if there were 10 other classes that need access to that store and want to change it: adding those new features is becoming a pain because all those classes are now at the mercy of the other classes mutating the state from underneath them, forcing you to handle tons of edge cases not possibly even related to the business logic of those classes.
With the flux style architecture, all those classes will only consume data from the creatureStore and dispatch actions based on that state. The store handles reconciling the different actions with the state so that all of its subscribers have the right data at the right times.
The benefits of this pattern may not be evident when you only have a couple of stores that are consumed by one or two entities each. When you have tens (or hundreds) of stores with tens (or hundreds) of components consuming data from several stores each, this architecture saves you time and money by making it easier to develop new features without breaking existing ones.
Hope this wall-o-text helped to clarify!
What I don't see is how putting the actions in the form of JSON objects, suddenly enforces or helps with "1-way" communication flow, which is what I read everywhere is the big advantage of having actions, and of flux.
Looks to me like I am still effectively sending a message back to the store, no matter how I perfume the pig. Sure the action is now going through a couple of layers of indirection (action creator, dispatcher) before it gets to the store, but unless I am missing something the component that sends that action for all practical purposes is updating whatever stores are listening for the kill message.
What I am missing here?
Facebook Flux took the idea from the event driven GUI systems.
In there even if you move your mouse you get messages. This was called message loop then, and now we have actions dispatching.
Also, we have lists of subscribers inside stores.
And it is really the same principle in Redux where you have one store, while in Flux you may have multiple stores.
Now little mathematics. Having 2 components A and B you need to have just a few possible update chains A updates B and B update A, or self-update (non including in here the updates from outside of the app). This is the possible case.
With just three components we have much more possible chains.
And with even more components it gets complicated. So to suppress the exponential complexity of possible components interaction we have this Flux pattern which in nothing more than IDispatch, IObservable if you worked with these interfaces from some other programming languages. One would be for spitting the actions, and the other for entering the listener's chain that exists inside the store.
With this pattern, your React code will be organized in a different way than common React approach. You will not have to use React.Component state anymore. Instead, you will use the Store(s) that will hold the application state.
Your component can only show the desire to mutate the application state by dispatching the action. For instance: onClick may dispatch the action to increment the counter. The actions are objects with the property type: that is usually a string, and usually in upper case, but the action object may have many other props such as ID, value,...
Since the components are responsible for rendering based on the application state we need somehow to deliver them the application state. It may be via the props = store.getState() or we may use the context. But also check this.
Finally, it is even not forbidden that component uses the internal state (this.state) in case this has no impact on the application. You should recognize these cases.
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 have an application which receives messages from a server every 1 second and via socket.io these messages are broadcast to a react component.
My react component is using a flux style architecture, so it calls an action when a message is received which adds (or updates) a record in a collection in a store.
My component monitors changes on the store and then updates the UI when it changes. In this case, it draws a marker on a map.
The problem is that due to the frequency of the updates my component redraws all markers every second, which I don't want.
I'm looking for an approach to allow my map component to respond to changes in a collection in a store, but not have the state update every second.
I thought I could have a collection for the raw data, and then only update the networkGPS collection selectively in my store, but the component seems to change based on any property in the store as they all seem to be part of its state.
In summary I'm looking to:
Collect data every 1 second in raw form and add this data to a store.
Bind a component to a collection in this store which I update only when changes require a UI redraw.
What I think I need to do:
Either:
Avoid putting the raw data into the state of the store (at the moment I'm unsure how to declare a collection which is not part of the state)
OR
Make my component more clever as to when it redraws, so it doesn't try to redraw on each state change.
I'm not sure which is the most appropriate solution for the problem, any info would be gratefully receieved.
Sorry if the question is a little unclear, I'm new to react / flux. Please do ask for any clarifications.
EDIT
At the moment I am getting round this issue by storing an array of the items (map markers in this example) I want my component to display in the component itself :(
When the state changes and render is called, I intercept the state properties, decide if anything needs to change in the collection of markers and only amend them if needed. If no changes are needed, the collection in the component stays the same and no redraw is performed.
This is not ideal, I feel like I should be able to allow my component to respond to only specific state changes but I've not found it yet, still looking for a better answer.
There are a couple of ways to approach this:
1) Use shouldComponentUpdate to compare old props and new props, re-rendering only the new marker states.
2) Switch to a cursor-based state tree that automatically triggers renders only for views that are bound to the specific parts of the state tree that have changed. I believe Baobab is one such library. See also Om (written in ClojureScript) and probably a number of other Flux implementations.
Given the impact of #2 my vote would be to go the route of #1.
I am not quite sure what's the problem that you face with updates every second.
However, the one place where you can queue the updates is between the socket.io and calling the action.
Instead of calling the action every second, you could queue the updates before sending it across to react.
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.
According to Flux Architecture View uses Action to call Dispatcher that updates the Store, while View listening to Store change events.
My question is: Why do we need Store?
In order to list all users, my Component will call ListAllUsersAction that will in turn call my API and will update the Store with the result of API call. Store then emits change event that the View is listening to. But the store also stores the result. Why? Why this middle layer is needed? I wont call the store directly anyway, so this cache layer makes no sense to me, and as I generate more events that loads more data, eventually all my stores will have all the state of my application because flux architecture says nothing about cleaning the Stores.
Am I missing something?
The goal of Flux is to keep data flow easy to understand even as apps becomes large and complex, such that a new person can be brought up to speed quickly, figure out what's going on by inspecting the source code, and be confident that they can make changes without breaking things. Modularity and separation of concerns are a big part of that. The Stores are a way to keep the data models independent of the details of the view layer and establish a single source of truth for the application state. You can look at any Store's code and see what data it holds, what actions it responds to, which dependencies it has for data in other Stores. It's a matter of organization for the sake of the developers, at the cost of code being slightly less compact.
In order to list all users, my Component will call ListAllUsersAction
that will in turn call my API and will update the Store with the
result of API call.
Since the function of Actions is mainly to provide updated data to stores, you could also just call the API first and then just create one Action to handle the result.
as I generate more events that loads more data, eventually all my
stores will have all the state of my application because flux
architecture says nothing about cleaning the Stores.
Holding the current state of the application is the intended function of the Stores. If user actions or API calls cause the data to change, the Actions notify the Stores and the Stores responsible for keeping that data update accordingly (maybe even being reset to null). There's no need for any other sort of cleaning, because the Stores "having all the state" is exactly what they're supposed to be doing.
Stores are in charge of application state and logic, so for example, let's say you fetch all users through your ListAllUsersAction, you get an array from your API
var users = [{firstName: 'LIMELIGHTS'}, {firstName: 'SKWEE357'}];
Now, the users name are apparently capitalised as your API decides that this is the way to deliver the data.
This just won't do so you want to fix it.
Using just React or just the Action where would you put this code, where would it make sense?
In your view, your dispatcher or your action? No, you definitely don't want to clutter your React component with this type of logic.
Nor does it make sense to do this data manipulation in the Dispatcher or Action, they are after all just notifiers that something should happen.