Some great ideas, especially the examples of breaking down UI into smaller components. Thanks for writing this.
I've been using Flux to manage pretty much all of the application state, and pass it to the top-level component and down through the tree of components as props. This idea was very well described in an older article[0]. I would add that this approach makes shouldComponentUpdate very important, since more of the tree is rendered on every change.
Your example of the dropdown state is a great counter-example where Flux is not necessary or even helpful, and in fact I've used the same example when explaining it myself.
One thing I'm not sure about is whether the save action should use a callback (or Flux ActionCreator), or whether it should be triggered by comparing the values in componentDidUpdate as you suggest. Making that behavior declarative is certainly appealing, but I'd be worried that an action like a save could be idempotent, with unpredictable side effects, and therefore makes more sense to be explicit/imperative? I'm not sure which one would be easier to work with at very large scale, and it looks like your application is larger than any of mine, so perhaps the declarative style works better. If anyone has more to say about this I'm curious to hear opinions.
I like me some React, but my only gripe w it is the synthetic events, which can result in a single model's state being updated by multiple listeners.
I have replaced synthetic events w event streams, and couldn't be happier w how much cleaner the code has become as a result of replacing setters with stream-combinators.
The architecture roughly follows Elm's Model-View-Update, by splitting each component into view.jsx and update.js.
1. Update contains the eventstreams (replacement for synthetic events), and are transforms them into update-streams.
2. The model combines multiple update streams to return a model-stream.
3. The view combines multiple model streams, and the subscriber at the end does a `setState` to trigger the re-render
Using streams also has the side-benefit of not needing the didUpdate, shouldUpdate etc lifecycle hooks.
> Flux is also quite verbose, which makes it inconvenient for data state, state that is persisted to the server. We currently use a global Backbone model cache for data fetching and saving but we’re also experimenting with a Relay-like system for REST apis. (Stay tuned for more on this topic).
What do they mean by this? I _think_ I've been doing something similar on my projects lately. Using Meteor as my backend pub/sub model, and React.js as my front end. Ties together pretty nicely although there's a range of other issues I'm still trying to work out (1 test frameworks for both React and Meteor code, etc..)
This is a guess, but it sounds like if a user opens some persistent record for editing, the data for that record (once loaded to the client) is not moved into a Flux store but is kept in a cache layer. Then the store would only contain the information that is necessary to render the UI.
My approach is to have a "mapper" module that is responsible for translating to and from API data transfer objects, and then an AJAX module that loads state into and saves state from the stores, using the mapper module. This means that all data the client may need to reference ends up in the stores, even if that data may not be displayed on the screen. However it eliminates the need for another cache layer on the client.
One question I'm still working through is whether a UI interaction with a low-level component should be allowed to cause state not typically included in that component to save to the server. In this case, maybe one Flux action would dispatch just to tell the stores to collect the needed data and then dispatch another action to save, or perhaps the stores would call the AJAX service directly, or the action creator could request the state from the stores and save it with the AJAX service immediately. I like how simple the "every step is a new action" approach is, but it could lead to a lot of extra functions that don't do much except move the data along.
"If you find yourself duplicating/synchronizing state between a child and parent component, then move that state out of the child component completely."
I have exactly one exception to this rule and I would love is someone could provide a recommended way to not make it an exception. Debouncing. For example, if you have a input field that lets you type a number for pagination, debouncing the change of that value with the displayed copy of it.
This is implemented with a debounced input field type that uses props to get and update the value, but maintains it's own state for what is displayed. It uses `componentWillReceiveProps` to stay in sync.
EDIT:
After re-reading, it looks like the author uses this exact same exception for the "select". This is considered UI state and should be fine.
Please don't promote your project repeatedly in threads. It's fine if you have something specific to say about it that naturally forms part of the conversation. But artificially injecting it into conversations isn't ok.
never understood why React needed flux on top. Or does that mean React doesn't solve the problem it is supposed to solve on its own ? or flux should have been baked in react? never understand what flux was about anyway.
Flux is a way to organize interactions with data and business logic, so that React only has to worry about the UI. Specifically, it breaks down behaviours into functional streams that don't interrupt each otherr.
[+] [-] guscost|10 years ago|reply
I've been using Flux to manage pretty much all of the application state, and pass it to the top-level component and down through the tree of components as props. This idea was very well described in an older article[0]. I would add that this approach makes shouldComponentUpdate very important, since more of the tree is rendered on every change.
Your example of the dropdown state is a great counter-example where Flux is not necessary or even helpful, and in fact I've used the same example when explaining it myself.
One thing I'm not sure about is whether the save action should use a callback (or Flux ActionCreator), or whether it should be triggered by comparing the values in componentDidUpdate as you suggest. Making that behavior declarative is certainly appealing, but I'd be worried that an action like a save could be idempotent, with unpredictable side effects, and therefore makes more sense to be explicit/imperative? I'm not sure which one would be easier to work with at very large scale, and it looks like your application is larger than any of mine, so perhaps the declarative style works better. If anyone has more to say about this I'm curious to hear opinions.
[0] http://aeflash.com/2015-02/react-tips-and-best-practices.htm...
[+] [-] k__|10 years ago|reply
[+] [-] findjashua|10 years ago|reply
I have replaced synthetic events w event streams, and couldn't be happier w how much cleaner the code has become as a result of replacing setters with stream-combinators.
The architecture roughly follows Elm's Model-View-Update, by splitting each component into view.jsx and update.js.
1. Update contains the eventstreams (replacement for synthetic events), and are transforms them into update-streams.
2. The model combines multiple update streams to return a model-stream.
3. The view combines multiple model streams, and the subscriber at the end does a `setState` to trigger the re-render
Using streams also has the side-benefit of not needing the didUpdate, shouldUpdate etc lifecycle hooks.
Here's a gist w the eventstream code: https://gist.github.com/findjashua/e78063e6591a2c234919
Happy to answer any questions.
Edit: special thx to Andre Staltz for the insightful discussions.
[+] [-] jxm262|10 years ago|reply
What do they mean by this? I _think_ I've been doing something similar on my projects lately. Using Meteor as my backend pub/sub model, and React.js as my front end. Ties together pretty nicely although there's a range of other issues I'm still trying to work out (1 test frameworks for both React and Meteor code, etc..)
[+] [-] guscost|10 years ago|reply
My approach is to have a "mapper" module that is responsible for translating to and from API data transfer objects, and then an AJAX module that loads state into and saves state from the stores, using the mapper module. This means that all data the client may need to reference ends up in the stores, even if that data may not be displayed on the screen. However it eliminates the need for another cache layer on the client.
One question I'm still working through is whether a UI interaction with a low-level component should be allowed to cause state not typically included in that component to save to the server. In this case, maybe one Flux action would dispatch just to tell the stores to collect the needed data and then dispatch another action to save, or perhaps the stores would call the AJAX service directly, or the action creator could request the state from the stores and save it with the AJAX service immediately. I like how simple the "every step is a new action" approach is, but it could lead to a lot of extra functions that don't do much except move the data along.
[+] [-] chowes|10 years ago|reply
The Relay system they're talking about is in reference to http://facebook.github.io/react/blog/2015/02/20/introducing-..., which is how Facebook manages data fetching at the component level.
[+] [-] porker|10 years ago|reply
[+] [-] hellbanner|10 years ago|reply
[+] [-] baddox|10 years ago|reply
http://jjt.io/2014/07/30/building-a-board-game-with-react-js...
[+] [-] ArthurClemens|10 years ago|reply
[1] https://github.com/ornicar/chessground [2] http://lichess.org [3] http://mithril.js.org
[+] [-] mikemintz|10 years ago|reply
[+] [-] vkjv|10 years ago|reply
I have exactly one exception to this rule and I would love is someone could provide a recommended way to not make it an exception. Debouncing. For example, if you have a input field that lets you type a number for pagination, debouncing the change of that value with the displayed copy of it.
This is implemented with a debounced input field type that uses props to get and update the value, but maintains it's own state for what is displayed. It uses `componentWillReceiveProps` to stay in sync.
EDIT:
After re-reading, it looks like the author uses this exact same exception for the "select". This is considered UI state and should be fine.
[+] [-] Sir_Cmpwn|10 years ago|reply
[+] [-] unknown|10 years ago|reply
[deleted]
[+] [-] dmk46|10 years ago|reply
[+] [-] d_luaz|10 years ago|reply
[deleted]
[+] [-] dang|10 years ago|reply
[+] [-] aikah|10 years ago|reply
[+] [-] WalterSear|10 years ago|reply