Hacker News new | past | comments | ask | show | jobs | submit login
A SoundCloud client in React and Redux (robinwieruch.de)
253 points by rwieruch on June 12, 2016 | hide | past | favorite | 69 comments



There seems to be a big blind spot in these Real World React+Redux tutorials when it comes to forms.

For example, say I have a page with reviews on it. So there's a Component that handles editing a review. Where does the state for all the form inputs go? Does it go in the Redux store? If it does, then every time the user types a letter you get an event, which goes walking backward through the component tree until it hits the review editor Controller, which generates an action, which dispatches to the store, which gets reduced causing a state change, which then percolates back up through the Controller/Component tree to finally add that letter to the input. That walking back and forth is bad enough on its own, let alone generating an entirely new state (because the state is immutable) for every single letter the user types. It's inefficient, and it adds noise into the action log.

The alternatives seem to be storing the state in the component in which the typing is happening, or in the parent Controller, and then only generating update actions against the store when some "finished typing" event occurs (i.e. onBlur or timeout). Of course this seems to go against the idealistic implementation of Redux where all the state is stored in that one state object and it's immutable.

My point being, I haven't stumble across a "real world" tutorial that goes over this stuff. I wouldn't quibble about that normally, as I'm happy to play around and find the solution on my own, but this feels like such a common and important task (what website doesn't have forms of some kind?) and the performance and maintenance implications of how this is implemented seem large. So it would be nice if there was a "We built this huge web app using React and Redux, this is how we ended up doing forms and it works well for us" article.


We use Redux where I work and we deal with a TON of forms, many of which are controlled completely separately from each other. Oh and it's an Angular 1 app and in production.

Basically what I've learned is that you have to distinguish between global and local application state. Redux doesn't have to know EVERYTHING that's going on in the application.

That means that when you're editing a piece of text, it depends on what you want from it. Are you showing word count in several places that aren't contextually related? (like a total word count up top on your page, and a local word count). If so, run it through Redux on every key down/key up event. Are you NOT using that information anywhere? Then skip Redux and pipe that edited text through only when a user hits "save".

The entire point of Flux was to have one source of truth for shared data in your app. If you're not sharing information, it doesn't have to be shoved into Flux/Redux. The example the FB developers brought up was reading messages. You have like 5 places that unread message count shows up: the little chat bar on bottom if you haven't read a message from a recent contact, notification bar up top, chat sidebar, and left sidebar (I think). Doing anything in any of those places should trigger a change: unread count drops when you open a chatbox with a person that left you a message for instance.

But if you're writing a message and you're looking at your character count (like on twitter when you're writing a tweet), that data doesn't need to be shared. It just stays in that textarea component.

I hope that makes sense! :)


Autocomplete is another good example. Instead of having my autocomplete API call go through the whole dispatch -> reducer flow, I just update my component's state when I get a response.


Yay! Another person using Redux with Angular 1. I've found it's a surprisingly nice combination. :-)


This is something that tripped me up when I first started learning Redux too, and it would be great if there were more articles about what to do with the bits that don't really fit nicely into the model.

My personal take is that the Redux state tree is for managing state that other components care about, i.e. if this changes, does something outside of this need to know? In the form example, there are few cases where we care about what is being typed as it is being typed (maybe autocomplete). We generally only care about the complete input. This could even be taken to the next level and we could say we only really care about the contents of a form once it has been submitted.

To think about it another way,: what state would we like to keep if the page was refreshed? Many UI elements may contain state we don't really care about outside of the component, such as drop-down menus, accordions, etc. There are things which constitute global state, and there are things that really are just local state. Why make them global?

Ultimately, the goal of Redux is to make reasoning about the state of an application easier, and to encourage better architecture. If pushing certain bits of state into the tree makes this harder, there's no reason to try to force it to fit the pattern.

I'm sure there are good arguments for why everything should be in the global state, but I just haven't found it to be practically beneficial.


I agree with your point and disagree with redux-form being a valid solution. redux-form creates a very restricted and crippled API for getting form data into a redux state object and does so at the expense of the spirit of React's componentized model. Any redux-form state needs to be isolated under a special root object specifically for forms. You can't just mount some existing part of your state object into a form because of this. Not to mention redux-form properties have a whole bunch of other "glue" properties in addition to the actual value of the form.

I question whether people use it in large scale production. This is one of the huge downfalls of redux. It makes some things easier, but it makes the most fundamental stuff (editing form fields) extremely contrived. This is the main reason I don't see react+redux as a true answer to the challenges of SPA development but a very rough, intermediate step which needs to be re-thought while putting into consideration the most common use cases.


> I question whether people use it in large scale production.

We tried to, and removed it within 4 weeks of development with it, for all of the reasons you mentioned. It conceptually doesn't map to our Redux model, and new developers struggled with it because of that. I ended up writing my own "wrapper" to do something similar, but to be honest for most of our form state it is handled within the components themselves (via this.state and friends) and only sent to Redux once validated. React's built-in state handling is still useful, specifically for these sorts of things.


What are the pros/cons of setstate vs a batched state subtree for each component and an aggregate state subtree for each component that matched each form parent, specified by a string to a higher order component for the parent form and a string for each component used to model html5 form inputs?


Ah... you lost me on that statement. Can you give me an example of what you're picturing?


I'd be interested in seeing what you put together. Is that available somewhere?


Not yet, but it will be!

https://github.com/studionone/ is where it will live, plan is to release it this week, all things going well.


Cool. I'll keep an eye out for it!


Also tried redux form and while the creator is very talented it was just more trouble than it was worth for our team. There's a real opportunity to create a sound abstraction to forms with react and redux right now. I've investigated this heavily and no existing approach works how I'd like.

What I want is a higher order component for a given form parent specified by a string, with higher order components for all common html5 form types which are stored in a state tree child node based on a string for the form parent and each component which encapsulates form elements, and rolls up small change events to avoid event log spam.

I originally thought a json or other dsl solution would be ideal, but that ends up being an integration and interoperability headache. Jsx as a dsl works better than json as a dsl for ui in my experience. The big question is where to store the "batched" changes before being rolled up.

I think rather than component setstate, perhaps a sub tree in the redux store with its own subtrees could exist with a specific prefix that could be hidden in the redux devtools chrome extension to avoid the spam problem. I rather like this solution, as it would make react and redux forms beat the simplicity of angular 1 forms with all the benefits react and redux bring. Would other people use it? Anything like it exist already? Maybe I'll create this...


You should take a look at https://github.com/davidkpiano/react-redux-form - solves many of your problems listed (batches actions when possible to prevent log spam), and V1.0 will be even simpler/more performant.


I did after reading your comment. Sweet approach and some quite clever algorithms in the implementation. Nice work! I'll have to play with it. If it's not quite the api I need maybe I'll open an issue to discuss a potential pull request. Thanks :-)


I've used it in a couple of form-heavy production projects and found it to be good, I find the API pretty easy to work with and it handles things like async validation pretty nicely (I use it in combination with yup for validating the form object: https://github.com/jquense/yup). I can honestly say it has almost made working with forms enjoyable ;)

A definite high point of my time with it was when a requirement came along to allow users to resume signup - because all the form state was in the Redux store already, it was simply a case of serializing that and putting it in localStorage (or wherever), then reloading it as the initialState.

There are performance issues to be aware of if your form gets too large, caused by the "top down re-render" effect of having the top level ReduxForm HOC connected to the form state and therefore re-rendering on every keystroke - often these can be solved by splitting the form into sub-components with appropriate shouldComponentUpdate etc., but this isn't a perfect solution - see https://github.com/erikras/redux-form/issues/123 for a lot of discussion. The new v6 API aims to solve this, by removing the need for a single top level component: https://github.com/erikras/redux-form/tree/v6

I've been intending to write up my experiences and how I've been working with it, but decided to hold off until the v6 API stabilised, so that I wasn't giving outdated advice. Happy to answer any questions in the meantime!


FWIW redux-form v6 changes this: https://github.com/erikras/redux-form/releases

I've been using redux-form in large production app, btw, and have mostly found it nice, but am looking forward to migrating to the new version.


Most of the time I don't feel like performance is an issue as long as all your components use PureRenderMixin. Unless it takes over 20-30ms to render, the difference is not noticeable.

The problem as others have said is that it pollutes history, and that it is very CPU cycle hungry, bad for anything running on battery.

Nowadays I tend to use onBlur instead. There are two scenarios:

1) The form is the only source of change for the data. In this case just update the global state onBlur.

2) Other things affect the form value, so you need controlled input. In this case, onChange updates the local state. onBlur updates the global state and set the local state to null. Use `<input value={localStateValue !== null ? localStateValue : globalStateValue}/>`


I'm working on it, I promise!

I have been doing a LOT of work on forms in React and Redux, and have been putting all of my ideas into a library called React-Redux-Form: https://github.com/davidkpiano/react-redux-form

The premise is simple - your model state shouldn't be intertwined with your form state, and everything should be "vanilla Redux" -- that is, everything related to form state should be represented by actions and reducers, and not by some black-box API hidden inside a decorated component.

Version 1.0 is still in the works, but promises a lot of things such as making it MUCH simpler to get up and running with configuring the Redux store for forms, and also making smart optimizations that greatly improve performance.

My goal is to make forms as easy to implement as it was in Angular, and offer full flexibility to the developer to handle forms of various complexity.


One small nitpick

> let alone generating an entirely new state (because the state is immutable)

Assuming you're talking about the redux state, this is only if the state changes. Dispatching an action to change the state of your form should cause the form's entire state tree to change at most, and possibly as few as one or zero objects. All those other redux reducers that just return the previous state are not going to have any performance impact.

In effect, your state is immutable but you're only getting "new" state references for a small portion of the state atom, very cheaply.


It all depends on what's going on in the render method and/or shouldComponentUpdate.


For basic forms, where you're not doing something weird, why would intermediate form state need to live in Redux? Local React state seems best for that.


Are there any cons to this approach? For me, having to implement setstate manually, not having full undo/redo/time travel debugging as an option/not being able to rehydrate the app from any state snapshot, along with the cognitive overhead of keeping state in sync in two places, one of which is mutable, are the big downsides.


I agree with this. I've tried redux-form and just manually handling it with Redux but letting the form data live in the form's component state felt the most clean to me.


Your concerns about input events moving up and down the component tree seem misplaced. It’s what happens when you use React anyway (unless you’re only using local component state); putting these input values in the Redux store only makes things simpler.

As for the fact that it changes the state and adds noise to the log… these seem like aesthetic judgments on your part. Why is changing an input “noise”? It’s an important part of the data on the page.


No, it's a pretty valid concern. If you assume controlled inputs, and you also tend towards putting all your state into Redux, that's exactly a use case issue you can see - every keystroke would, in theory, require a full round-trip through the store and back to the component, and also causing at least a `mapState` call and comparison for every other connected component along the way.


Here is a GIF of me putting input state (and general form state) in the Redux store: http://i.imgur.com/QRDH1Sy.gifv

Yes, if the keystrokes are not debounced it generates many actions. But my point is that concern about the ‘efficiency’ of moving events through components when using Redux is nothing compared to the byzantine way I had this implemented before using Redux. Trying to build forms in React without Redux or some other state solution, by just using setState and moving events and state up and down the component tree, will make you tear your hair out.


For me the batched changes are useful, but single character changes can make the event log overly verbose.


For the downvoter, you don't find single character changes overly verbose in the redux devtools monitor?


First: Redux is _not_ dogmatic about putting every last bit of your state into Redux. It is entirely up to you how much or how little goes into Redux. (This question comes up all the time, which is why I added it the the Redux FAQ [0] ).

So yes, there are absolutely times when it makes sense to keep data, such as frequent user input updates, in local component state, and only dispatch an action later. In fact, a while back I put together a wrapper component that does exactly that [1]. Made a few tweaks since then that I haven't added to the proof-of-concept in the gist, but the basic idea is there.

I do have a number of articles on React and forms in my React/Redux links list [2], but at the moment I don't think any of them specifically deal with Redux usage. Might be interesting to look through some of the apps listed in my real-world Redux apps list [3] - the Jenkins UI and Wordpress Admin Page apps might be relevant.

I will say that while Redux Form [4] is probably the most widely used Redux form logic library at the moment, React Redux Form [5] is another option that looks good. In fact, the current Redux Form 6.0 beta API is heavily inspired by RRF's API. My Redux addons catalog also has a page listing a number of other options of varying kinds [6] (none of which I've used myself, btw, just catalogued).

All that said, yeah, would be great if someone were to write up a larger, more in-depth article about dealing with forms.

[0]: http://redux.js.org/docs/FAQ.html#organizing-state-only-redu...

[1]: https://gist.github.com/markerikson/554cab15d83fd994dfab

[2]: https://github.com/markerikson/react-redux-links/blob/master...

[3]: https://github.com/markerikson/redux-ecosystem-links/blob/ma...

[4]: https://github.com/erikras/redux-form

[5]: https://github.com/davidkpiano/react-redux-form

[6]: https://github.com/markerikson/redux-ecosystem-links/blob/ma...


100% agreed, the current standard seems to be redux-form but I've never heard of any serious production build using it.



Everyone basically just uses Redux Form (http://redux-form.com/5.2.5/) as near as I can tell; it really works well.

It basically works as per your first guess: Everything goes in the store. And no, it doesn't really have any performance or efficiency issues. Try it out and see. :)


The overhead for a simple textarea seems significant. On Firefox there are stalls (up to 500 ms) if I type "fast". I've tested the form on http://redux-form.com/5.2.5/#/examples/simple?_k=8ukhm8 (specifically the textarea).

Here is the CPU time graph during my tests: http://imgur.com/we9uGQ7

EDIT: OK apparently it's not accurate, see comment below.


Confirmed; just tested on my machine (Firefox, i7-3632QM @ 2.2GHz) by mashing on the keyboard. CPU spikes to 100% on one core and results in stalling.

In contrast the React+Redux app I'm working on uses the local Controller/Component state method and mashing on the keyboard there gave ~20% CPU.

I never would have imagined that Redux Form would lag on a desktop/laptop. I was more concerned that mobile performance would suffer. It's kind of impressive that a simple form can lag a desktop browser...


He has redux dev tools enabled, hence the performance issues.


Indeed it behaves much better on a demo I found elsewhere, sorry for the inaccurate comment.


For improved performance (especially on big/complex forms) I suggest looking/using v6 (technically still in alpha, but stable).


While a super cool and important project, it's a ton of work to integrate into an app when you have maybe 3 sets of radio buttons to deal with, such as when doing an incremental migration refactoring.


>it's a ton of work to integrate into an app when you have maybe 3 sets of radio buttons to deal with

Untrue. It easily reduces the amount of code in your component, and actually gives you the ability to make your FormComponent as a stateless component.

https://gist.github.com/sorahn/6fac9f4717a81c96cd97e2bcc86a1...


Having worked with Redux on a few different projects I'd probably recommend a different approach than the one outlined in this tutorial.

One of the great things about React is the advice that you can put more in a single file than you used to be able to. We seem to have forgotten that concept when it comes to the data layer of most Redux applications I see.

I would suggest that people check out the Ducks pattern: https://github.com/erikras/ducks-modular-redux

To sum it up: your constants, action creators, action handlers, and reducer all go in a single file. You can export anything you need from a duck file, and the default export for any duck must be the reducer. This ends up being really nice - no more rapidly switching back and forth between action creator files, reducer files, your constant files, etc. It also allows you to easily collect all your ducks and attach them to a redux store (thanks to the default export.)

Some suggest putting any data fetching in the ducks as well - I think this can be handled on a case-by-case basis - I tend to prefer to keep my data managers in a separate "services" folder though, which are just files that export async functions like "fetchUser(id)" or "fetchArticles()"

Ultimately there are many different valid ways to structure your code, this is just one way that I've found to be much more enjoyable.


While Redux is much better than early Flux implementations, I feel that both have the same issues: quite a lot of boilerplate, scattered logic and confusion around best practices. Don't get me wrong, I really like Redux but I feel that most of the time I'm writing either boilerplate code or trying to decide how to structure my actions and reducers. Also combining actions/reducers with components isn't particularly easy given that you still need to combine reducers into one state. Not to talk about cherry picking parts of state into props and then working with reselect to cache values or normalize API responses...

Oh sorry, I think I had something that I needed to let out :)

Anyway, recently I discovered MobX[1] which, aside from the bad name, is amazing. It's pretty flexible and has very small API surface but compared to Redux it just gets out of the way. Granted, there's a lot of hidden logic which feels like magic but in the end it allows me to build apps way faster while still producing performant code. I have moved few projects from Redux to Mobx and it's mainly removing code and files and bundling mobx stores with components. At work I been able to explain it to coworkers in two minutes where as it has taken hours or days for people to understand Redux. I highly recommend checking out the React+MobX tutorial[2] and give it a go even though it's not the hot thing of the week just yet :)

[1] https://mobxjs.github.io/mobx/ [2] https://mobxjs.github.io/mobx/getting-started.html


Don't map your reducers/actions to the components... map them to the data/features they are tied to. If you organize by feature, you don't really have to have your actions/dispatchers sitting with your components, but you can where it makes sense.


Just to expand... as an example...

    .../features/users/login
may contain both the ui components as well as specific actions/reducers related to login... though other components may call actions under features/user as necessary, because they correlate to the data/features/functionality around users, where the components themselves may be for other features.

I feel that once you stop trying to segregate by either component or type of module/script that it tends to be far easier for someone new to come up to speed in a project. It is taking practice and some thought, but has worked very well where I've implemented this approach so far.


Mobx needs more love.

For flexibility I am leaning towards making MobX as a choice in projects and then being able to build up towards an architecture like Flux in an application.


Second this, mobx is awesome


Heaven for bid you actually have to understand how/why something works and not just have "magic" happen.


Cool to see so much packed into one place. In my experience with React and Redux, there have been two major challenges in finding well-documented examples to follow. First is finding anything that describes more than a toy project that just demonstrates the most straightforward implementation of one-way data flow. This tutorial isn't bad, although it's comparable to the reddit-based tutorial in the Redux docs. The second challenge is finding documentation that is up-to-date with the latest versions of all the tools. This is even more challenging when the dependency versions aren't explicit - it would be very helpful to add this info up front in this tutorial, especially since Webpack 2 is just around the corner, and React Hot Loader 1 is deprecated in favor of the soon-to-be-release version 3 - dependencies in the Redux-React world are moving fast so all documentation needs to be 'pinned' to explicit versions.

It's also good to see a pretty complete description of the testing setup, although I have some concerns about the "inject everything into the `global` object in setup" approach, which is a relatively coarse technique for simulating a browser environment in Node, and means the unit tests are running in a "special" environment unlike the actual running application environment. No one has completely solved this problem, but it's an important part of the discussion for anyone wanting to do things "right" from the beginning. Injecting `React` as a global variable for convenience also makes me cringe a little bit, given the ease of writing explicit dependencies in modern javascript.

Nice step-by-step article though - I like that there are a growing number of accessible entry points like this and I look forward to the community growing and maturing to define best practices.


That's one of the reasons why I've been pulling together a list of full-blown open source Redux-based apps and larger-scale examples over at https://github.com/markerikson/redux-ecosystem-links/blob/ma.... Mozilla Firefox's debugger (current and future), the Wordpress Admin Panel, and the Jenkins UI all qualify as reasonably more than a Todo List or Reddit Viewer example.


Here's a recent and great React + Redux testing example using the AVA test runner, which doesn't allow globals:

http://silvenon.com/testing-react-and-redux/

Disclaimer: I am in the AVA team. It really is cool though.


Solid article. In addition, I keep a list of high-quality React and Redux tutorials over at https://github.com/markerikson/react-redux-links (as well as a Redux addons catalog at https://github.com/markerikson/redux-ecosystem-links). Working on an update to the tutorials list today, and this article's going in.


Nice tut, although I've learned one thing during past couple of months of developing with Redux: middleware is very useful and often quite an overlooked/underused part of the stack.

In this tutorial, the author makes the call directly in an action, which is not its responsibility. Here's an example of the "api" middleware we've developed (https://gist.github.com/tomazzaman/a98f5ad3f052592e564862e4d...), and the beautiful part is, this is the only file in which async calls are made. And it's a rather small file, which allows me to change to any ajax library (like the native fetch when it gets more support) I want at any time, with very little development.

Bottom line: middleware is awesome, give it a try. :)


Doing API calls inside async action creators / thunks _is_ entirely valid - in the end, it's still "creating actions". Just a question of how much duplication you want to deal with, consolidation, etc.

But yeah, middleware is a concept that isn't talked about as much, but on the flip side, there's dozens and dozens of middlewares people have created. In fact, there's at least a couple dozen middlewares I've seen _just_ for doing AJAX calls. See my list at https://github.com/markerikson/redux-ecosystem-links/blob/ma... .


And thunks for async get even sweeter with async/await/await*!


I look in src/ but nothing says SoundCloud client to me. I recognise the structure of this app as following current conventions but the logic is scattered by concerns I don't care about - e.g. I don't need a package called constants, I know what a constant looks like.

Concerns I do care about are hidden from me across files and package folders.


The past few days I have really been getting into the 3REE stack: React, Redux and RethinkDB and have been learning so much. Here's a blog[1] outlining it and a github example of it [2]. It has been great so far, what are some UI libraries that I should take a look at, so far this looks like the best option[3]

[1] http://blog.workshape.io/the-3ree-stack-react-redux-rethinkd...

[2] https://github.com/GordyD/3ree

[3] http://www.material-ui.com/#/


In my own app, I'm using Semantic-UI as the styling baseline, especially for inputs. There's a number of React wrappers around Semantic-UI, such as https://github.com/hallister/semantic-react and https://github.com/TechnologyAdvice/stardust, but most of them create React wrappers around Semantic's jQuery-based widgets. I'm trying to avoid use of jQuery entirely, so I opted for https://github.com/shinzui/react-semantic-ui-kit instead. Not as complete as the other libs, but a great focus on just wrapping the CSS/className aspects of Semantic-UI.

Beyond that, https://github.com/jquense/react-widgets is a great set of more specialized input widgets (dropdowns, datetime picker, numeric spinner, etc), and https://github.com/rofrischmann/react-layout-components is a good React wrapper around Flexbox usage.


I've been having fun using your stack along with RethinkDB's Horizon.io. Great way to get basic apps going!


At work I've started keeping the reducer in the actions file, anyone else do that? This way they can share the same const declarations, and you don't have to toggle between two files when most actions / reducers are pretty small.


Of course: https://github.com/erikras/ducks-modular-redux

The potential reason not to do that is that more than a reducer may listen for a given action, even though I guess it's more an exception than the norm.


https://github.com/loggur/react-redux-provide

This allows you to encapsulate all of your Redux actions/reducers/etc into standalone "providers". It decouples pretty much all of your React components (UI) from your Redux logic (models/controllers), which maximually separates your concerns and makes it trivial to reuse anything anywhere. It also allows you to instantly swap databases and/or websocket implementations.


I originally tried this but then discovered combineReducers() this will allow you to organise your reducers into a single import. I found it a more sane way to organise all of my reducers, as the majority of them were small. Here's a link for API http://redux.js.org/docs/api/combineReducers.html


That would combine your reducers into one. I believe he was referring to putting action creators in the same file as their associated reducers.


It seems that this is also the way of doing things in Elm (from which Redux is inspired): http://guide.elm-lang.org/architecture/index.html . The Update part groups both the definition of the actions and the reducers.


I started doing that pretty soon after starting with Redux, with some other tweaks to make them even more tightly coupled. It worked well for me, but I assume there is a reason not do that. However, I never figured that one out.


I do this as well in small projects, then split it out when things get big (eg; >1000 line file).


Here is another example of the exact same: a SoundCloud client written in React+Redux https://github.com/andrewngu/sound-redux http://soundredux.io



Looks very promising, thanks!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: