9 ms·
Simple React Patterns
- tw1010 9y agoPart of me is sad that Javascript has now been made OOP, after a majority of efforts pushing the balance towards becoming more and more functional (e.g. PureScript) only a few years ago.
- dunstad 9y agoReact explicitly discourages using inheritance with components, instead promoting composing components together like functions. You're also encouraged to keep them stateless if possible, so they create the same output for the same input. I'll need some convincing that functional programming is on its way out in JavaScript.
- personomas 9y agoYeah, but why did they switch from `React.createClass({..})` to the class syntax? Classes are terrible in JS, they're not even native JS, it doesn't fit in JS. This change alone has brought more and more OO JS code.
- personomas 9y agoIn React v0.13 https://reactjs.org/blog/2015/03/10/react-v0.13.html https://reactjs.org/blog/2015/03/10/react-v0.13.html, they claimed the reason for the switch is because the `class` syntax is for "more flexibility." What can you do with the `class` syntax that you can't do with the functional syntax? Furthermore, you can generate React code much easier with the functional syntax than with the class syntax. Also, the functional syntax has mixins and other ways to combine objects or share code, I think that would be harder to do with classes, which make them even less flexible.
- acemarke 9y agoAt the moment, React does not yet have support for stateful functional components. It's something they've said they would _like_ to research and implement down the road, but right now, component state and lifecycle methods requires class components.
- bjeanes 9y agoOr the use of something like recompose[1]'s `lifecycle` function[2]. 1: https://github.com/acdlite/recompose https://github.com/acdlite/recompose 2: https://github.com/acdlite/recompose/blob/8c4ac2e4/docs/API.md#lifecycle https://github.com/acdlite/recompose/blob/8c4ac2e4/docs/API....
- acemarke 9y agoYeah, although that itself is implemented internally using an ES6 class: https://github.com/acdlite/recompose/blob/8c4ac2e4a4cd8d60c8db9bc4cd73f0ce044fd9ca/src/packages/recompose/lifecycle.js#L16-L25 https://github.com/acdlite/recompose/blob/8c4ac2e4a4cd8d60c8...
- always_good 9y agoSeems like a weird thing to miss for someone who ostensibly prefers functional programming. For example, people here will talk about `function User() {}` like it's the pinnacle of amazing abstraction. Because it has the word "function" in it or something. There's nothing functional about that to me. Mutating the prototype and dealing with the implicit `this` variable in your functions is about as far away from functional programming as you can get. Meanwhile Javascript just gets more and more functional, from the mindshare of higher order functions/components gaining traction to the @decorator transformation to the Promise 'monad'. It just gets better and better. I have a hard time taking anyone seriously who doesn't recognize the pain that `class` solved in the ecosystem whether you personally decide to use it or not. People have been writing OOP code in Javascript since day one, just with some of the worst idiosyncrasies of all OOP languages.
- 9y ago
- acemarke 9y agoPrior to ES6, there were hundreds of incompatible class-like implementations in the JS ecosystem, and every major library had its own. That included React. Now that ES6 classes are actually part of the language, there's no reason for the React team to continue maintaining their own class-like implementation. They can defer that to the language itself. This also enables better use of standardizing tooling around the JS language.
- cname 9y agoI thought `createClass` does/did something essentially similar to what `class` does in ES6. Not having to use a framework's custom class-creation machinery doesn't seem like a big loss to me or like it is has anything to do with functional programming.
- acemarke 9y agoYep. It also had some differences in behavior. `createClass` auto-bound functions so that `this` always pointed to the component instance, and it supported mixins. The React team now encourages functional forms of composition rather than use of mixins, so that's another reason why React.Component doesn't support them. The lack of automatic method binding has certainly been a major pain for people learning and using React, but it also means that there's no "magic" involved. The Stage 3 Class Properties syntax is the recommended way to ensure that methods are bound properly, and there's plenty of other possible solutions as well.
- recursive 9y agoWhat do you mean native JS? It doesn't get much more native than "given keywords and syntax in the language and in the specification"
- kowdermeister 9y agoClasses are perfectly fine in JS, they are just syntactic enhancements for managing prototypes more easily.
- vinnymac 9y agoNot sure what you mean by 'just syntactic enhancements'. Check this out https://esdiscuss.org/topic/javascript-vision-thing#content-5 https://esdiscuss.org/topic/javascript-vision-thing#content-...
- coldtea 9y agoClasses in JS6 are just a syntax sugar for the native JS prototype inheritance. Nothing "terrible", absolutely native (both since the keyword is part of JS and since the implementation is based on prototypes that have been with JS since the beginning), and fits perfectly.
- tmat 9y agoactually once redux is introduced to react it's very very very functional.. so hold the sadness
- azangru 9y agoReact is functional even without Redux :-) Look at the Reason team, with their Reason-React implementation. Cheng Lou seems to actively dislike a single store that Redux is, and, if anything, co-locates stores to stateful components.
- tmat 9y agoThat sounds more like flux, how do they avoid all the boilerplate that flux was? It was very very painful.
- christophilus 9y agoClojureScript's re-frame[0] is my favorite way to React, honestly. PureScript's halogen is also pretty nice. Re-frame has a built-in, functionally pure way to do ajax and side-effects, too, which comes in pretty handy. [0] https://github.com/Day8/re-frame https://github.com/Day8/re-frame
- escherize 9y agoI use reframes little brother, citrus with cljs and it has been awesome. The promise of data oriented frontend engineering really pays off. https://github.com/roman01la/citrus https://github.com/roman01la/citrus
- rlander 9y agoAwesome! I've used reagent/re-frame extensively for years, but I've never tried Rum because I couldn't find an obvious way to use it with re-frame. What do you think of citrus so far? Is it production ready? Stable? Anything missing?
- acemarke 9y agoThis is an _excellent_ summary of some of the most useful React patterns. I can also recommend "React Patterns" [0] and "React Bits" [1], which show a variety of other useful patterns as well. Beyond those, my React/Redux links list has sections on "React Component Patterns" [2] and "React Component Composition" [3], with links to many additional articles on these topics. [0] http://reactpatterns.com/ http://reactpatterns.com/ [1] https://github.com/vasanthk/react-bits https://github.com/vasanthk/react-bits [2] https://github.com/markerikson/react-redux-links/blob/master/react-component-patterns.md https://github.com/markerikson/react-redux-links/blob/master... [3] https://github.com/markerikson/react-redux-links/blob/master/react-component-composition.md https://github.com/markerikson/react-redux-links/blob/master...
- chrismorgan 9y agoHow does the pervasive use of higher-order functions affect debugging? Take this example: const withDagobah = ({ LoadingViewComponent, ErrorViewComponent, PlanetViewComponent }) => class extends React.Component { … }; In a language with proper generics, you’d write this instead: class Dagobah<LoadingViewComponent, ErrorViewComponent, PlanetViewComponent> extends React.Component { … } And then you’d clearly have an object of type Dagobah<LoadingView, ErrorView, PlanetView>, easy to inspect and debug. (And Dagobah<A, B, C> = Dagobah<A, B, C>, whereas withDagobah(A, B, C) ≠ withDagobah(A, B, C).) (I’ll ignore the topic of type erasure.)
- acemarke 9y agoIf you inspect the component tree using the React DevTools, you'll be able to see exactly what values each component is passing to its children, and what their internal state is. So, use of HOCs adds an extra level of nesting to the component tree, but it should also be very clear in there what each component is passing down.
- notaboutdave 9y agoThat ~30 line component should be reduced to something resembling this: const PlanetView = ({planet}) => <div>{planet ? planet.stuff : 'Loading...'}</div>
- internet_rando 9y agosteve-carell-thank-you.gif
- always_good 9y agoIt's demonstrating a component that needs I/O, so I'm not sure how refactoring it into one that needs its parent to do the I/O helps the demonstration.
- justtopthings 9y agohttps://www.youtube.com/watch?v=RiVkFDSPqfs&t=337s https://www.youtube.com/watch?v=RiVkFDSPqfs&t=337s
- blumomo 9y agoThe Container/View Pattern in this article, isn't this MVC?
- Cthulhu_ 9y agoYou could argue it's a variation thereof, more like MVVC (Model View ViewController) maybe. In React you only really have the View layer. In this case, there's two views, a 'functional' view (the ViewController) and the 'presentation' view (the View itself). I guess the main difference is that in MVC, the view and controller are two separated entities (side-by-side), while in React it's embedded (Controller wraps around View).
- jbaudanza 9y agoThe problem with resolving promises inside of `componentDidMount` is that there is no way to cancel those promises. If the component is unmounted, and then setState is called, it will generate an error (or a warning. I can't remember). Facebook discusses this on its blog: https://reactjs.org/blog/2015/12/16/ismounted-antipattern.html https://reactjs.org/blog/2015/12/16/ismounted-antipattern.ht... I've been using Observables which, in my experience, bind a little cleaner than Promises to React components.
- Guillaume86 9y agoYes there was a whole discussion in a github issue about the correct pattern to solve this. Creating and guarding against a `isMounted` field in your `.then()` callbacks means you prevent garbage collection of the unmounted components. An easy solution is to use cancellable promises (with some subtleties to allow garbage collection). There's an implementation which take care of this properly here (provides an HoC): https://github.com/hjylewis/trashable-react https://github.com/hjylewis/trashable-react .
- loevborg 9y agoWhen you say "cancellable promises", that means that you cannot use native JS promises, correct?
- Guillaume86 9y agoYou can, you just need to wrap it when used inside a react component.
- TekMol 9y agoThis part seems strange. What is it supposed to do? componentDidMount() { fetch("https://swapi.co/api/planets/5") .then(res => res.json()) .then( planet => this.setState({ loading: false, planet }), error => this.setState({ loading: false, error }) ); }
- Secretmapper 9y agoIt fetches data from an endpoint then sets component state based on the response
- TekMol 9y agoWell, it makes a http request but it seems to do nothing with the data returned.
- Secretmapper 9y agoThe data is set as component state .then( planet => this.setState({ loading: false, planet }), error => this.setState({ loading: false, error }) );
- pdyck 9y agoWhen the component is mounted on the page, a planet is fetched from an api. Then the response body is parsed to JSON. After that the object is added to the component state. When there was an error it is added to the state instead.
- TekMol 9y agoThen the response body is parsed to JSON The "res => res.json()" call? But what does this do? res seems to be used nowhere and just discarded without any side effects.
- arianvanp 9y agoThis is a shorthand for function (r) { return r.json()} Note the extra return. We return the JSON decoded response that is our planet. and use its value to set state if no error occured
- methyl 9y agoThe author claims the article [1] says render props are anti-pattern, while this is not true. The only thing that's called an anti-pattern there is "children as a function". If you use properly named render prop other than children, they seem to be fine with that. Will be good to correct that since it's pretty confusing. [1] http://americanexpress.io/faccs-are-an-antipattern/ http://americanexpress.io/faccs-are-an-antipattern/
- fky 9y agoAfter 2+ years of writing React most of the day every day with a lot of different patterns coming in and falling out of love in our app, our experience falls almost exactly in line with this. It's simpler than I would have imagined from when we first started using React/Redux and handles the most complex interfaces our app has. My favorite part is how he models the different states the component can have. He describes the different states in a comment block but we found it even nicer to use Flow and disjoint unions to help the developer avoid impossible states which we learned from a talk by Jared Forsyth: https://www.youtube.com/watch?v=V1po0BT7kac https://www.youtube.com/watch?v=V1po0BT7kac. Another huge boon in productivity and so simple and obvious in hindsight!
- masklinn 9y ago> He describes the different states in a comment block but we found it even nicer to use Flow and disjoint unions to help the developer avoid impossible states I'm on the highway to Elm <insert music note because HN hates unicode> (or reason, or purescript, either way towards a language with first-class sum types support, that's just so convenient)
- shamas 9y agoCorrect me if I'm wrong, but these patterns don't seem very much like a Redux way to do things. A lot of things feel somewhat implicit too. For example, I stopped just spreading my state into the child component, opting to be explicit nearing verbose instead.
- mercer 9y agoWhile I'm not sure it's a 'Redux thing', I've also become more conservative in my 'spreading'. I'm not necessarily against it, but too often the resulting code started feeling too alien and dense. The former is bad for working with people new to ES2015+, but the latter started bothering me even in my own projects.
- christophilus 9y agoHis example: const withDagobah = PlanetViewComponent => class extends React.Component { ... } } Seems as if it would be pretty bad from a perf perspective. It's defining a class every time that function is called. Anyone familiar with the dark underside of JavaScript care to comment?
- kitd 9y agoA class is just a constructor function (in this case the function is a React component), so it is a function returning a function, which is a fairly common activity.
- PuercoPop 9y agoIt is only going to be called once (per componented used in) upon loading the page. No biggie
- boubiyeah 9y agoIt's evaluated once at script parsing/evaluation time so it's not really expensive.
- barrkel 9y agoTwo things I don't like about the approach taken with these specific examples (and they're related): - It's harder to see the structure of a page when the parts are composed functionally instead of structurally (i.e. explicit nesting); but functional composition is required because the next step down from components are DOM elements - This approach makes consistent styling of a large application difficult because it ties business domain views to low-level details like specific DOM elements (I don't disagree with the patterns themselves, per se. But I wouldn't make them domain specific.) I'd prefer to code up pages using components designed around a higher level of abstraction: more like UI widgets, and less like business domain specific views. That gives two benefits: increased consistency because there are fewer ways to compose higher level abstractions for a given screen complexity; and it's possible to use nesting more directly, since the nesting won't be so verbose, because the raw materials are higher level abstractions than <span> and <div>, they're more like <DropdownMenu> and <MenuItem>. (This approach is what we do where I work, only it's been built out of Backbone because that was what was current at the time. It's basically MVVM where the viewmodel is the model for a UI component, rather than anything in the domain of the business. An early anti-pattern in Backbone was to have one-off views for every individual little bit of the page; we found this to make understanding the whole composition very awkward.)
- amk_ 9y agoCSS-in-JS libs like https://emotion.sh https://emotion.sh make "micro-componentization" even easier - pretty much every raw DOM element can be replaced by a domain-relevant/ui-relevant component type. Then if you need to add something beyond styles like state or complex render logic, the consumers don't even know about it because the reference is the same.
- MPiccinato 9y agoI am always intrigued when I come across an article like this, after having done 2+ years of React development, and have never seen these patterns (Provider Pattern), nor seen them debated (Render Props). I do pay attention to the community here and there, and constantly poke around open source react libraries. I tend to bring in patterns from iOS development, such as Delegates, especially when using React Native, and is really useful when using Flow or TypeScript. We heavily rely on the Material-UI library (https://material-ui-next.com/ https://material-ui-next.com/) and have brought a lot of their patterns into our libraries, mostly how reusable components are composed.
- deleted 9y ago[deleted]
- wuliwong 9y agoI just heard about the Provider pattern this week when reading about using react-router and mobx. That being said, it is a solution to a "problem" that I noticed fairly early on in my react usage.
- jihokoo 9y agothis is awesome, so easy to understand. looking forward to more!
- js4all 9y agoFetching data directly in components is bad advice and leads to a program with cluttered data access becoming more and more unmaintainable as the app grows. You need to encapsulate fetching and storage in redux or a similar state management solution.