I am building a simple React application with the following component hierarchy structure:
--- ReqList (shows a list of Request Items)
------ ReqListHeader (A div that has list title and some buttons)
------ ReqTable (list of request items)
--------- ReqRow (one row of request)
------------ ReqItemHeader
------------ ReqItemBody
------------ ...few more components in between
--------------- ReqEditor
------------------ TextArea
The root component receives data via ajax call and calls setState(data), as per the standard docs of React, and all is well, and this works correctly. I am able to show all descendant components and their values.
However for form input (textarea), the input doesn't change. I understand that I have to implement a change handler. The thing that I am unsure about is, where to implement it?
Option I:
I initially thought, I would just add a changeHandler function in its immediate owner, by calling setState() on it. But I am afraid that I am breaking the uni-directional data flow principle by introducing a state in a middle level component.
Option II: Should I pass a changeHandler callback as a property from say "ReqRow" component all the way down to ReqEditor, so that it could be bound to textArea change event?
Option III: Or should I start at the root of the hierarchy i.e. ReqList? That is because, even ReqRow receives the data from its owner and its parent and so on, since that is the entry point for data.
I am not too sure, the level of upwards propagation that I should do in this case. Most of the examples are simple ones with two level hierarchy. I am sure this can be done, but involves passing down the change handler callback all the way from to its grand child.
Is this correct, or is there any other better way?
The problem your facing is solved by using any of the Flux pattern implementations. React only provides a solution for the data rendering part, but it does not provide any clue on how to handle the data flow.
Flux is a pattern proposed by Facebook that tries to structure your app so changes in the data are manageable in a clean way.
From the Flux documentation:
All data flows through the dispatcher as a central hub. Actions are provided to the dispatcher in an action creator method, and most often originate from user interactions with the views. The dispatcher then invokes the callbacks that the stores have registered with it, dispatching actions to all stores. Within their registered callbacks, stores respond to whichever actions are relevant to the state they maintain. The stores then emit a change event to alert the controller-views that a change to the data layer has occurred. Controller-views listen for these events and retrieve data from the stores in an event handler. The controller-views call their own setState() method, causing a re-rendering of themselves and all of their descendants in the component tree.
This dense paragraph is a summary of the solution to your problem.
EDIT: You can also employ baobab, a library that offers an inmutable tree of data that emits events when some field (or its children) are updated. So each component listens to changes on the fields that it needs. There is a package for integrating it with React. I've used it and works great.
Related
I'll mention in advanced that this is not a technical question on how to do the data update between 2 sibling components, rather what is the correct way to do so.
I have the following component tree structure:
App
|
-- Home
|
-- SearchBar
| |
| -- Filters
|
-- ItemsList
ItemsList has logic in it to load the list of items from an API call and show the list of items on the page. It also manages the state of the articles. If an article is deleted it removes it from the list and updates it's state.
SearchBar is a component that contains a textual search that is displayed above the list where a user can enter text to search and also has a button that opens the Filters component where the user can filter different parameters. Once the user search's or filters the list in the ItemsList component should be updated accordingly.
There are a few ways I can think to achieve this:
Using react context - The provider will be on the Home component (or another component for holding SearchBar and ItemsList - and both SearchBar and ItemsList will contain a consumer that will updated the state with a method in the provider - those updating the components. This in my opinion create some dependency between those components and they are not really standing on their own (the ItemsList component should be used in other pages as well - of course this is still possible, but yet does not feel so "clean").
The ItemsList component will contain public methods such as "delete item", and "clear list" and those methods will be called from the home component. The SearchBar will get a property with a function as an event - something like "onFilterChanged" and will call the method on ItemsList (I will need to hold a ref to that component). But working like that feels like each component can stand and be re-used on it's own merit - but loosing the "reactiveness" and more wire up that needs to be done.
Are there any other ways to achieve what I'm looking for that I'm not thinking of?
What is the correct way to architect this kind of solutions?
When two siblings need access to the same state, keeping the state in a mutual parent component is a common and recommended way of handling it (the official React documentation encourages that approach for most cases). That way, both the state and any methods to update the state can be passed to both children as needed, and any updates made by one child will be reflected in the other child.
There can be drawbacks to that approach: Depending on the size of the application, prop drilling can make code confusing and difficult to maintain—especially if one component that is using said data is being used in multiple places, deeply nested, or both.
For such occasions, holding the state in context or redux is a more appropriate approach. The first option that you listed is completely legitimate and what I would recommend. Maybe extracting your context entirely (not keeping it in your Home component and instead creating the context in its own file) would help things feel more "clean."
Determining when to use which approach is something that comes with time and experience. When choosing an approach, it is helpful to keep your future plans in mind. If you know the application will be small and simple, keeping the state in a parent component is a great move. If you have big plans for your application, using context or Redux from the start will be easiest.
Thankfully, the worst case scenario is that you decide to change from one approach to the other, which can always be done (confusing and tedious as it may be at times).
I seem to have encountered a situation where I cannot avoid the dispatch-within-a-dispatch problem in Flux.
I've read a few similar questions regarding this problem but none of them seem to have a good solution besides setTimeout hacks, which I would like to avoid.
I'm actually using alt.js instead of Flux but I think the concepts are the same.
Scenario
Imagine a component that initially renders a login form. When a user logs in, this triggers an XHR that eventually responds with authentication information (eg. the user name), and then fetches some secure data based on the authentication information and renders it instead of the login form.
The problem I have is when I attempt to fire an action to fetch data based on the XHR response, it is still in the dispatch of the LOGIN_RESPONSE action, and triggers the dreaded
Error: Invariant Violation: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.
Example
I have created this jsfiddle to demonstrate the problem.
I have a Wrapper component which either renders a login button or a Contents child component, based on whether the user is set in MyStore.
First, the login button is rendered in Wrapper component.
Clicking the button dispatches the LOGIN action.
After a delay, the LOGIN_RESPONSE action is dispatched (via the async mechanism in alt.js).
This action triggers MyStore to update the user name.
Wrapper component observes the store change and updates its state.
This causes Wrapper to render Content component instead of the login button.
Content component, on mount, attempts to dispatch the FETCH_DATA action, which fails because the dispatcher is still dispatching LOGIN_RESPONSE. (If I wrap the FETCH_DATA dispatch in a setTimeout it works, but it feels like a hack).
Variations of this seems to be a common scenario. In fact almost all the related questions have a similar scenario, but with no good or concrete answers.
React - Authentication process : Cannot dispatch in the middle of a dispatch
Dispatching cascading/dependent async requests in Flux/React
Flux Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch
Is there something intrinsically wrong with this data flow? What is the proper Flux way of doing something like this?
This is a common problem with dispatching in componentDidMount in many libraries. The solution is to wrap dispatches in React's batched updates; luckily, Alt allows you to do this with the batchingFunction option:
var alt = new Alt({
// React.addons.batchedUpdates is deprecated:
// batchingFunction: React.addons.batchedUpdates
// use this instead in newer versions of React
// see https://discuss.reactjs.org/t/any-plan-for-reactdom-unstable-batchedupdates/1978
batchingFunction: ReactDOM.unstable_batchedUpdates
});
See https://jsfiddle.net/BinaryMuse/qftyfjgy/ for a working example and this Fluxxor issue for a description of the same problem in a different framework.
I believe our loyal friend, the Dispatcher, has its right to complain.
I will try to describe a hypothetical situation before throwing my conclusions. Let's say an app has two stores S1 and S2 and two kinds of actions A1 and A2. The right flow of an usual Flux implementation should be something like:
Component fires an action A1 (basically a dispatch);
Single dispatcher distribute correspondent payload to all registered stores;
S1 consumes the payload and maybe updates its state;
All components listening to changes in S1 check for changes they are interested in and maybe update their internal states (possibly triggering an re-render);
S2 consumes ... (like in step 3)
All components listening to changes in S2... (like in step 4)
Now all stores are done dealing with the action payload, components can fire new actions (A1 or A2).
One of the greatest advantages of using Flux over traditional MVC is that Flux gives you The Gift of Predictability. This feeling empowers the developer in such a way that they believe that, by correctly applying the Flux philosophy, they are sure that the order of execution is always somewhat similar to:
A1 > S1 > S2 > A2 > S1 > S2 > ...
This is a really great deal, especially when trying to find sources of bugs. One could argue that enforcing a predictable chain of events can lead to some inefficiency and he is probably right, especially when dealing with async calls, but that is the price you pay for having such a great power!
Due to the async calls, things can get a little messy. Something like the following chain of events could happen:
A1 > S1 > A2 > S2 > S1 > S2 > ...
Maybe your app can handle such chain of events quite well, but such "unpredictability" hurts basic motivations behind Flux's unidirectional data flow.
I feel there is no consensus in the community on how to handle such situations, but I will share my general solution: "for the sake of predictability, make sure to not trigger any new actions before you have the last one totally processed".
One way of doing this is by downloading all the necessary (new) data the app needs to re-render successfully before triggering any additional actions. In your example, this could be achieved by first downloading the data involved in the LOGIN_RESPONSE and FETCH_DATA actions and wrapping it in a single payload and then dispatch it, so all the components will have the data they want already in the stores without asking for more.
Say I have an action someAction(params) that takes params which is managed in a store paramsStore:
paramsStore.listen(function(params) {
someAction(params)
})
It seems that I can't just call this in my view because apparently this goes against the Flux way of doing things (actions shouldn't be called within store listeners).
The reason I have someAction inside the store listener, is because I want it to be called every time the paramsStore is modified. How can I achieved this without resorting to the 'unpattern' of calling actions within stores listener?
The right "flux way" of doing it would be to call the someAction(params) wherever information is dispatched to paramsStore.
Understanding what someAction does will give more clarity. Does it really need to be an action? If you're just doing some manipulation in the store data, you could have it as a local method in the paramStore.
While I am new to flux as well I could offer a suggestion. State that is needed to determine the outcome of an action that is held by Store A could be attached to a get method. This state can be retrieved by a View with a getter. When the action is called this state can be sent as a parameter. If something needs to be async it can now be done here (ajax call or something else) based on what the state is. Either the result of this or a promise object can then be used to trigger an action which is passed to the dispatcher. The dispatcher sends the result or promise to the store. The store then updates its state and the process repeats as necessary (when initial action is triggered).
I think a little more detail of what exactly you need would help actually. I do believe listening for for an action and triggering another action inside the store doesn't coincide with flux. I do think there is likely a way to accomplish the actual result you want using flux but without more detail this is the best I could come up with. Also, in reality you can implement anything you want. Flux is just a model and by extension a self imposed constraint to help with structure.
If you are using Flux as is, you could refer to the original image of the whole architecture at https://github.com/facebook/flux.
As you can see not only views could create actions. There are also Web API Utils which could create ones. Generally speaking not only API utils can do this. It's totally okey to create actions in order to start some behaviour according to outside world, some services or something else.
But in your case you are trying to create an action on some store update listener. As far as I can understand this would result in some changes in one or few other stores. In this case you probably don't need to create an action in the listener, but rather create some relations between your stores with waitFor API. Here is a link with detailed information: http://facebook.github.io/flux/docs/todo-list.html#adding-dependency-management-to-the-dispatcher.
I'm setting up a site using react.js. the client receives a massive json through ajax that is then used to populate all the necessary fields, graphs, etc. Seeing as this json will cover pretty much all the information contained on the site, it has be accesible in almost every single component. Is there a clean way to pass it to every last component (and there are a lot of them)? Passing it the regular way to ~70 components seems dirty and inefficient.
As React's documentation states, you could have components communicate by integrate a global event system, and then subscribe to an application-data event from all your components, in componentDidMount().
This way, each time you will emit the application-data event from within the code responsible for pulling the website data, all components will receive that data. At that point, you can call setState().
Please be careful and unbind the events once a component goes "out of scope", inside componentWillUnmount().
Failing to do so will result in memory leaks, as you will have the event handler dangling, and it will be called each time you pull the website data, even if your Component's instance has been removed from the DOM.
You could also try to make your components pull data by themselves, as sending a huge JSON around, is not the best solution. I mean, each component should use the data it needs, in order to work, not the whole website data. The way I would alter this would be by parsing the JSON object and storing it for reference in a variable, on a scope that is accessible to all components, and use the event system to only notify the components that the data is ready, and each component would go to the global data object and get their data.
Example(pseudo-code):
XHR.getData
XHR.onReadyState => GlobalNameSpace.data = data
EventSystem.PUBLISH('application-data')
// component code
Component {
EventSystem.SUBSCRIBE('application-data') =>
dataNeededForInit = GlobalNameSpace.data.componentXData
}
Reference: https://facebook.github.io/react/tips/communicate-between-components.html
Context might fit the bill. It might not be particularly tidy solution but perhaps that would be a starting point for you.
What is the advantage of using Flux over a global event bus? I think the dispatcher is all that is needed:
component publishes 'user event' with data to the dispatcher
dispatcher executes handler of the subscribed store
handler publishes 'update event' with the store's updated properties
dispatcher executes handler of the subscribed component, and updates component state with the store's updated properties
What am I missing here that I can't do without Flux?
I think what others have said about application structure and the change event is important, but I should add this one thing:
The dispatcher's waitFor method is the biggest difference between registering the stores with a dispatcher vs. the stores listening to a global event bus. This method lets you manage which stores update before others. And that becomes vital when you want StoreB to look first at what StoreA did before it decides what to do.
You could think of the dispatcher as a global event bus with a waitFor method, and that would be somewhat accurate.
I'm not an expert in flux but an architecture doesn't enable you to do something that wasn't possible before, it gives your application a structure that is extensible and understandable.
I believe it's all about code structure which is understandable even in large scale.
Supose you have appState which holds underlying data for components.
The components call action. Action is responsible for gather data from XHR or modify the incoming data from component and then it dispatch complete data to subscribed store.
Store is the only part of your code, which can modify your appState and it is basically the only thing, what it does. It takes data from action and store them to appState or removes some data from appState according to action.
Then you fire stateChanged event, which your component should listen to and will rerender.
So you have all action specific logic in actions. You handle appState only in stores. And that should help you keep your code understandable.
Flux pattern
My understanding of why is good idea to dispatch only complete data comes mainly from this article. And it is based on official Facebook Flux diagram
The advantages of this approach are:
stores are simple and synchronous, does not contain decision logic, just handles given data
there is no need to fire another action in store, which will break one-directional chain of Flux
dispatcher is the single channel for all state changes, it knows what action with what data is processed, so its easier for debugging
You basically described flux, the only difference is:
stores emit a change event
And the component updating its state isn't part of flux, that's a common practice for integrating flux and react.
Flux just names each of these pieces and gives guidelines on what each piece's responsibility is.
It's essentially a main event emitter (dispatcher), the event types (actions), functions that emit an event on the dispatcher (action creators; the event body is a payload), and other event emitters that: keep state, listen to the dispatcher and emit change events (stores).
At least that's how it works in JS. The core principle is the unidirectional data flow. There are plenty of event emitters that are used for bidirectional communication.