Hacker News new | past | comments | ask | show | jobs | submit login
Show HN: Build your first real world React.js application (plot.ly)
261 points by mxstbr on Sept 7, 2016 | hide | past | favorite | 44 comments



Nice tutorials. I've been playing around with React, and I like it overall... But I can't wrap my head around Redux. Could someone explain it to me so I might finally see the light?

My problem is this: I just can't understand why I'd want to manipulate state in an unsightly switch statement like this one from Chapter 4 of the original post:

  switch (action.type) {
    case 'CHANGE_LOCATION':
      return Object.assign({}, state, {
        location: action.location
      });
    case 'SET_SELECTED_TEMP':
      return Object.assign({}, state, {
        selected: {
          temp: action.temp,
          date: state.selected.date
        }
      });
    case 'SET_SELECTED_DATE':
      return Object.assign({}, state, {
        selected: {
          date: action.date,
          temp: state.selected.temp
        }
      });
    default:
      return state;
  }
This reminds me of Win32 event loops with their window message handler switches... And that's a scary association.

What's the point with dispatching these fragile "stringly-typed" selectors, when you can just have real functions doing setState() updates on the relevant component?

I'm not trying to be snarky -- I know I'm missing something major here.


For a simple app your code works fine. You will have trouble when it scales.

We have migrated a load of model logic from Angular to redux and redux saga, once you get your head around a nice structure then its a dream.

I created a gist for you to check out [1]. Maybe also check out the concept of Redux Ducks [2]. I now find our code easy to read, share and test - unlike our massive Angular app.

Also as somebody else comments, you should make sure the only way you talk to your store is through a reducer dispatcher.

Regarding component states, find some reading on components v containers [3] - we even have 2 separate folders for the different types. Once mastered and implemented, most components are small and again easy to test.

[1] https://gist.github.com/anonymous/a20f603f9922742bd3cea7a08c...

[2] https://github.com/erikras/ducks-modular-redux

[3] https://medium.com/@dan_abramov/smart-and-dumb-components-7c...


I don't know. It looks like a lot of boilerplate just to tag a tiny bit of metadata onto a function pointer...

I guess I'll need to do some more reading on how this works in larger applications with more complex model structures. Thanks for the links!


FYI, I've got links to a number of actual Redux-based applications at https://github.com/markerikson/redux-ecosystem-links/blob/ma... , including Jenkins' new management UI, Firefox's new debugger, Wordpress's new admin page, the popular HyperTerm terminal application, and more. Twitter's mobile page is also built with React and Redux.


We have an pretty large production app w/ React & Redux.

In some ways it is initially a lot of boilerplate for various state reducers, but after everything is there building really functional features is extremely quick with redux; just from a developer's time point of view.


It's unfortunate that the Redux docs have led many (including myself) to think that Redux reducers means "switch statement". See http://redux.js.org/docs/basics/Reducers.html under "Note on switch and Boilerplate".

Your reducer can be whatever you want it to be as long as it does not produce side-effects. I use something like https://65535th.com/move-away/

Additionally, as roughcoder points out elsewhere in thread, using the object spread syntax (available in babel-preset-stage-2 or babel-plugin-transform-object-rest-spread) minimizes some additional boilerplate.


Just for the record, the Redux docs already describe this pattern: http://redux.js.org/docs/recipes/ReducingBoilerplate.html#ge...


It's nicer for more complicated applications.

Looking at that code block I know immediately that there are only 3 ways that the state can change and what happens during each transition.

This code is easy to unit test and verify that it is correct meaning that as long as the frontend components dispatch the right actions the store will contain the correct state.

Since the UI should just be a function of the store this means that there is no guess work when it comes to reasoning about what the rendered view will look like after each action.

I'm working on porting an angular SPA to react/redux. While the amount of code increased the whole thing is a lot easier to understand. The angular version had a lot of race conditions that I could never fix.


But is the UI just a function of the store? (I guess "store" in this context means the model data that is persisted on the server -- sorry if I'm getting the terminology wrong.)

For example, something like selection typically isn't part of the model. (Users don't expect to undo a selection in an editor.) That means components will need to manage that kind of transient state internally, and the whole "UI is just a function" paradigm kind of breaks down.


The "store" is purely data that is on the client. What data you put in the store is up to you. It may be a mixture of domain data from the server ("what items do I have in cache"), application-specific data ("the current selected item is #42"), and UI state ("there is a modal that is visible, of type ItemEditor"). It's certainly possible to put every single bit of data in your app into Redux, but there are definitely reasons to want to keep some of it in local component state. The classic example is "a dropdown is open" - other components probably wouldn't care about that, and it's not something you'd want to persist or toggle during time-traveling. It's up to you as a developer to decide what state should live where.

There's a couple recent articles that cover this topic: https://medium.freecodecamp.com/where-do-i-belong-a-guide-to... and http://jamesknelson.com/5-types-react-application-state/ . The Redux FAQ also discusses this: http://redux.js.org/docs/FAQ.html#organizing-state-only-redu... .


The UI can be purely a function of the store state, or not; whichever makes more sense in your project. I find it useful to use transient component state (ie. setState) also, for things like you mention - selecting something or keeping track of the "isOpen" state of a dropdown. Any state which does not need to live longer than the UI itself.

> the whole "UI is just a function" paradigm kind of breaks down.

Not really, it just goes from "UI is a function of the store state" to "UI is a function of the store state AND the component's internal transient state". Since internal state should only be modified by the component itself calling setState, this doesn't really add much complexity - in fact it usually reduces app complexity by obviating the need for a complicated global state structure for storing eg. the open state of every possible dropdown on the page.


> when you can just have real functions doing setState() updates on the relevant component

Using Redux, all or almost all of your state is stored in a global repository (the "store").

Those strings should probably be constants instead, especially for IDE auto-complete purpose, but you are never generating those manually.

Instead, your components would call "action creators", who are properly named functions, which in turn will dispatch one or several actions to the store.

In your component:

    actions.changeLocation(someLocation)
In your action creator:

    changeLocation(location) {
        dispatch({
            type: CONSTANTS.CHANGE_LOCATION,
            payload: { location },
        });
    }


Is it acceptable to dispatch inside your action creators? I'm somewhat new to redux but I got the impression that dispatch should be used inside mapDispatchToProps.


It's an example of the thunk pattern. Thunks are used when you have an async call, e.g. getFoos. Then your action creator would be

  function requestFoos() {
    return { type: REQ_FOOS }
  }

  function gotFoos(foos) {
    return { type: GOT_FOOS, foos }
  }

  export function getFoos() {
    return function(dispatch) { 
      dispatch(requestFoos());
      myFooProvider
        .getFoos()
        .then(function(foos) { dispatch(gotFoos(foos)); });
    }
  }
Really useful for writing reducers that want to keep track of when you're fetching data from the server.


Yes, but in the parent comment a thunk wasn't being dispatched, but rather a closure around some unknown dispatch function is being called.


I found that redux only made sense once I started using redux-thunk to dispatch and handle quite a bit of logic inside the action-creators.


My question was because if you look closely it isn't actually a thunk.


Amusingly, there was an article last year that made _exactly_ that comparison with WndProcs : https://bitquabit.com/post/the-more-things-change/ .

I wrote an answer on SO recently that describes some reasons for using Redux, which I'll paste here:

[quote]

Off the top of my head, a few advantages:

- A lot of the time your app's state tree could be considerably different than the UI tree

- Many components may need to access the same state and display it in different ways

- Hot reloading components will wipe out your existing component tree, including any state stored inside of them. Keeping the state separate from the UI tree allows the UI tree to be swapped out and reloaded with the updated components, while keeping your current development state the same.

And that's before getting to many of the commonly discussed benefits, such as predictable state updates, time travel debugging, improved testability, and centralized logic.

It's certainly true that you can write an entire application using nothing but React's component state (and Dan Abramov himself says that people often jump into Redux too early), but from my perspective Redux is absolutely worth it.

[/quote]

As for the switch statements: many of the patterns for Redux usage boil down to "serializability", which is a big deal because it enables scenarios like time travel debugging, persistence of app state, and more. I wrote some thoughts on that topic yesterday, at https://www.reddit.com/r/reactjs/comments/518qdr/anyone_have... .

FYI, I maintain a big list of links to high-quality tutorials on React, Redux, and related topics, at https://github.com/markerikson/react-redux-links . You may want to read through some of the Redux tutorials to get a better grasp on how it works and how the pieces fit together.


First, this code could be cleaned up a little. The action types should definitely be constants -- something like ActionTypes.CHANGE_LOCATION. Using lodash's _.defaults is also a lot more terse than Object.assign. And if you're a real stickler for minimalism: https://github.com/acdlite/redux-actions#handleactionsreduce...

The real power of reducers is composability. For example, I would almost certainly store the "selected" part of the state in a separate reducer. In my personal experience, reducers rarely respond to more than 3-4 actions.

As for the reason to do this instead of setState -- the whole point is to completely encapsulate state into something that's reproducable, trackable, and removable. Almost all of the components you write should be "dumb" -- that is, they just take properties and render some JSX. Then you have a few "smart" components who know about the state and send the data down to it's "dumb" children. Redux/Flux allows your application to be as stateless as possible, which is where React really shines.


If you support ES6/ES7 in your build cycle then we are big fans of the spread operator which can be used instead of _.default, Object.assign. For example:

    case 'CHANGE_LOCATION':
      return {
         ...state,
         location: action.location
      }


Note that object spreading is neither an ES6 nor an ES7 feature. It is currently at stage 2 - https://github.com/tc39/proposals#active-proposals "Rest/Spread Properties"


I'm curious. Do you not like the switch because it is dispatching locally and thus might end up a with a giant switch statement or because of the String type?

Or in other words is that you would you prefer OOP polymorphism dispatch or is just that Javascripts switch case statement is weak?

Part of the problem is Javascript doesn't have ADT aka structural pattern matching which is a far far far more powerful switch-like dispatch. Elm does (maybe TypeScript as well?). But OOP dispatch (and I suppose other polymorphic types besides inheritance) are a valid way as well to do this.


I find this string pattern terrible as well. As the application grows, there are lots of reducers with lots of strings, and it gets really confusing. It feels like this is inherited from Flux (https://facebook.github.io/flux/), Facebook's initial solution to handling state.

On the other hand, you have all the state logic on reducers... If actions were to dispatch state-modifying functions, it wouldn't take long for the code to become really confusing.


I'm not sure why everyone is getting hung up on the fact that this is implemented with strings and a switch statement. Who cares? Use constants. If JS had enums we would use them, but it doesn't.

The point is that there are a finite, well-enumerated set of possible state changes, which are all localized in one place: the reducer. Whether you call them "DO_THING" vs. constants.actions.DO_THING (which could be an integer or whatever you want) or whether you use a switch statement vs. if block vs. dictionary lookup is entirely up to you.


I'm not sure why but a lot of developers think or are taught that switch statements are old fashioned or even bad.


Probably because programmers new to OO tend to use them when inheritance/interfaces would be cleaner (case cat, case dog, etc.)


Facebook still uses Flux, and it was created specifically for large applications. The thing which is missing from this example is some kind of namespacing. In a large application, you should be naming your action types in such a way that it is not confusing which application feature they relate to, eg. if you had domain logic relating to something called 'UserProfile', you might have an action like `USER_PROFILES/UPDATE_PHOTO`. Combined with sensible project layout and file naming (eg. by domain) this should make it easy to track down code relating to a particular action. It also makes for excellent 'greppability'.

If you're using a JS typechecker like Flow (and IMO if you're building a large app, you should) you can even make use of disjoint unions to type your Flux action objects: https://flowtype.org/blog/2015/07/03/Disjoint-Unions.html


It's not so bad if you keep your exported consts in a file with JsDoc comments.


How would you do playback and automatic undo/redo without encapsulating your state transitions?


Instead of storing an action descriptor stack, you can also implement undo/redo by storing all past versions of the model on the server. I know surprisingly complex applications with large data sets that do this. The important point of course is that any large data should reside in separate immutable storage, so that the top-level model (which gets serialized for each action) remains as lightweight as possible.


Yes, AKA "poor man's undo."


Why ask the question if you knew the answer?


It is possible to use Symbols instead of strings, which yields some nice improvements in terms of no longer allowing construction of actions with potentially misspelled strings, etc.



You link to a comment which indicates serialization is a potentially breaking issue. This is a tradeoff that does not affect the routine behavior of redux but may affect advanced use cases such as allowing user upload of action history for bug reports.

Regardless of serialization/replay-ability, the core of the suggestion is to `import` some constant instead of using raw strings. Symbols have the benefit of forcing an import, whereas a list of constants that reference strings can be "bypassed" by writing the correct string in a random file. In fact, the redux actions documentation uses the following example of this pattern as the first example.

``` // fileA const ADD_TODO = 'ADD_TODO'

//fileB { type: ADD_TODO, text: 'Build my first Redux app' } ```


I remember reading comments about this in the past year or two - many people have seen the similarity between this and Win32 event loops...


Surprisingly few comments so far. My take: Max Stoiber is a fantastic contributor to the React ecosystem. His React-Boilerplate project is a solid, well-designed starting point for production application development. This new tutorial is well written, makes good use of the new Create-React-App tool to simplify the learning curve, and covers several topics in good depth to get someone started.


I find I am more productive with vue.js. It is much less intrusive on my coding style. I believe react.js is mainly popular because of FB's support. Much in the same way Angular became so widely used due to Google backing. I too have fallen for the myth that Open-Source projects backed by huge companies are always better. Do your homework. Don't believe the hype.


Would you mind sharing your rationale?

As someone approaching web front end development from a clean slate (es6 isn't the monster I though javascript to be, though there are some warts), can you tell me what vue does better than the alternatives (e.g., react)?

I'm building my first prototype of a web-based application right now, and sticking with raw ES6 to keep the cognitive load down (relative to javascript + a framework), but I'm likely to switch to one framework or another to get ride of some of the boilerplate I see happening.


You should try it out. There's no good way to answer the important questions with out putting in some effort. but... Vue.js is really fast. It does not require that you template your HTML in a special language like react. It works well with Jade and Stylus. It's really well documented. It's stable.


I just went through the first tutorial and it's well written. However, I just had to try it with AngularJs and jQuery as well. Here are the results http://goo.gl/V0NvkC

In such a simple example, I think jquery wins. However, more complicated solutions might be better suited towards frameworks like Angular and React.


The jQuery app may seem "the simplest", but it has no notion of components - https://jsbin.com/jupagugofe/edit?html,js,output, whereas the angular(https://jsbin.com/mepijibika/edit?html,js,output) and react(http://jsbin.com/gijigiqecu/edit?js,output) apps do.


If you ever find yourself in a codebase featuring a group of "full stack" enterprise developers, you'll find not a single component in an Angular codebase.


What I like that you've done is that start CTA in the right nav menu. Not just that it's action oriented it just made me feel like I could start/play around post-login without having to deal with a lot of redtape just to start using it.

Which for any new tool is important. Good luck.




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

Search: