Lessons learned as a React contractor
medium.com
medium.com
1. Wrap third party components, so you can swap them easily if they become unstable or you find something better. 2. If you have a codebase that predates create-react-app, take a look into its configuration, webpack is a complex beast and they've figured many optimizations. 3. Learn the concept of High Order Components and use them to abstract common functionality (for example, retrieving data from an API) 4. Even if you use Redux (I do and I love it), not everything belongs in there, use local state whenever it makes sense first. 5. Never, ever, depend on anything inside `react/lib`
Wrap _all_ third party library calls. This lets you reduce the scope of these calls to "approved" ways and make much stronger assertions about who's doing what in your code.
This gets super useful the further down the line you get to adding really complex features. Being able to isolate all the places where certain objects get created to add a permission check is nice.
It also makes testing easier because you have some nice entry points to mock.
Raw usage of things like an ORM can let you get through a prototype super quickly, but makes editing the application later on quite hard.
It's a shame it's really really tedious though!
Other times, no, it doesn't seem worth it. And I don't do it for every single external library I use.
Although it does tend to end up being most of them - which seems to be a consequence of the way I've generally done n-tier application design at work. I'm not always a fan of this architecture, but sometimes it does fall out nicely.
I had a senior developer suggest I wrap jQuery one time.
C# is a very, very different matter, and that's the perspective I was initially commenting from.
It feels like people have superiority complex over front-end devs and lately specifically javascript. It bothers me. What if you tell me your language of choice and every time I see it mentioned somewhere I will post some random "intellectual question" combined with stereotype that will make you feel stupid and just demotivate you a bit. Maybe I am missing something but your comment made me feel that way.
To answer your question fully; In my opinion it's because there is no single entity that owns the web. No one controls which things should be deprecated or continued, that's why it's happening much slower because big guys have to come to an agreement. Apple provides best practices for iOS, Google for Android,... MDN is a good resource but there is no official "BEST WEB PRACTICES" entity.
Although many patterns boil down to wrappers around objects (Delegator, Proxy, Adapter, Facade, etc.), but that doesn't make them all the same, even though their UML diagrams may look almost the same. The difference is: On which side is the wrapper? Which interfaces does it provide? Which interfaces does it use? Those details depend heavily on which architectural problem you want so solve (and it should be one you actually have). The pattern then guides you through how exactly that wrapper should look like in that particular case.
Thanks :)
I had my own autocomplete component that I would use wherever I needed an autocomplete, and that component would render the third party autocomplete and supply it with the props it required. When I needed to switch out which third party component I was using, I only had to change code in one place, instead of changing it everywhere I was using autocomplete.
lessons 1: there's a good chance, depending on what you are doing, that you do not need Redux - avoid it if you can. much docunentation seems to imply that it is a necessary hand in hand element of react development - not true. I have never found any situation in which I need redux.
lesson 2:' use "create-react-app", and if humanly possible, don't eject from it.
Sorry, I don't believe this is true. Redux's author himself, has explicitly written 'you might not need redux' (https://medium.com/@dan_abramov/you-might-not-need-redux-be4...), ostensibly to combat Javascript fatigue. Redux companion libraries often include similar notes as well.
I agree
> avoid it if you can
avoid using it in a project, sure, but don't avoid learning it. It definitely helped me level up as a developer.
My favorite resource for learning about redux: https://egghead.io/courses/getting-started-with-redux
For example, Preact, which in many cases can be a drop-in replacement for React at a fraction of the size, gives instructions on how to use it with webpack (to alias react and react-dom), but not Rollup.
The Webpack 2 guides are not great, but much better than what came before, and I'm sure many blog posts will continue be written on the subject. And ultimately it's not really that difficult to get at least a decent grasp of its concepts, and it pays off in the end.
The deepest pain point of JavaScript development is undoubtedly build configuration i.e. webpack and babel.
If you stick with create-react-app then you manage to remain in a zero build config world, which is a happy world to live in.
Really? Perhaps I've been doing this too long, but once you get your head round the basics I really don't think it's that bad
[1] https://github.com/markerikson/react-redux-links
[2] https://github.com/markerikson/react-redux-links/blob/master...
[3] https://github.com/markerikson/react-redux-links/blob/master...
create-react-app is a bundle of configuration. Configuration you'll likely need to override eventually. You're advising people new to the ecosystem blindly trust opaque configuration, and not bother understanding the underlying tools? This is terrible advice. (And if your argument is that you shouldn't use a tool without knowing how it works... Why use CRA at all? Write your own config.)
Keeping config directly in a project creates a maintainability issue once you have a number of projects on the go, as you then need to turn it into a module like create-react-app's react-scripts or else it's copy and paste time, which becomes horrible across multiple projects, especially if you're evolving your config as you learn.
Has anyone written a guide or supporting tools for creating your own config as a reusable module from the beginning? It feels like there's a gap which needs filled there.
It looks like you did!
https://github.com/insin/ad-hoc-reckons/blob/master/Creating...
Is it Ember/Vue-style "mutate, observe, compute" as implied by most of their example code or Flux-style as implied by their introduction visualization (https://github.com/mobxjs/mobx/raw/6ed404b96a8b4074a473accd2...)? Do I create actions or just mutate state directly (again, visualization and some text in the readme vs examples)? How is "observer turns React (function) components into derivations of the data they render." any different from just plain React components?
MobX feels like someone tried to make an easy state management solution (obviously a great goal) and in the process just threw together whatever pattern exist out there right now. It obviously works for many people, considering how often someone comments about it on HN, but I wonder what makes those user stick with React vs a framework that completely embraces this approach like Vue or Ember.
Is it some hard requirement on React?(EDIT: Or maybe preference of JSX?)
Nuclear-js feels heavy-handed, and there is indeed a lot of boiler plate, but in the end the explicit organization pays off in a huge way.
What most people don't realize is that state management will get insane past a certain point. Nuclear-JS can handle a truly massive application that is very complex -- think google docs/sheets
I imagine most people aren't building something like that though, and find it too cumbersome.
.. i'm also told that, if all you're doing is rendering templates, you might not need anything more than hyperx (a substack production).
1. Don't use a boilerplate. Not even create-react-app. Learn webpack and npm and TS / Babel. It's not that hard.
2. If you havn't yet, take a second look at MobX. I got it once I realized that I can organize my state just as I love it with Redux (normalized and update strictly through actions). With MobX you don't need Reselect.
3. Even though the React community seems to favor ES6, at least give Typescript a serious try.
I can't say that I agree with this
Hunkering down to learn build tools will probably yield the lowest ROI compared to nearly anything else you could be doing
They're nice to know, sure, but you'll pick them up along the way as you use and modify them
As you said, not that hard :)
And +1 on MobX. Bee's knees
Getting type information in your editor when you are several levels deep in components is very useful.
My only complaint is trying to figure out how to properly work with 3rd party react modules within typescript's own namespace system
Could you be more specific about the issue you're having here? Might be able to help
What's the story with dipping your toe into TypeScript on existing projects? I'm using Webpack and babel-loader, so I'm not interested in using the TypeScript compiler itself, having to renaming files to .ts, or using a different extension for files which contain JSX, but I'd still like to try TypeScript on a few modules. Is that possible?
Flow seems much easier to dip into, but I haven't tried either in anger yet.
Could you give some arguments? I'm a big ES6 fan and I can't think of a good reason to use Typescript over it.
I should write a blog post about Redux and Typescript - it would be good to know which bits you are struggling with.
HMR works fine with Typescript, you do need to use Babel as well though. This post I wrote should get you up and running: http://blog.tomduncalf.com/posts/setting-up-typescript-and-r..., it is a little outdated (1.9 vs 2.1) but I don't think any major changes should be needed to make it work with newer versions. Unfortunately, HMR (v3 beta) doesn't play nice with react-router as far as I can tell, so I have given up on it for now. I really miss it so I hope someone knowledgable finds time to focus on getting it working better soon!
Another general pain point (apart from the fact that a lot of the guides you find seem reasonably outdated) is the general build setup. Your guide seems really good, but generally setting up react redux and typescript is a bit of a jungle.
If you do end up writing a post, please let me know!
I don't know, it rubs me the wrong way when there are better tools out there. Mobx, is much quicker to integrate and is easier to understand how to integrate anywhere in a React app. Glad to see I'm not the only one who really doesn't like Redux.
I mean check it out, it has an incredibly small API, and even smaller if you use the right Webpack loaders.
import { observable, observe, toJS } from 'mobx'
export default class Store {
@observable foo = []
}
Then in your components just set or read from `this.context.store.foo`. Every component using the same store will update it's reference to foo.https://sergiotapia.me/using-mobx-and-react-to-build-an-inst...
I think too often people don't appreciate that redux, like all frameworks is a trade-off and is often the wrong tool for the job. Speaking about it as if it's a generic framework to trade out for another framework is a sign that it's being selected for the wrong reasons.
If it's not making your life easier, you probably shouldn't be using it for that application. It's a really nice framing hammer, not a multitool ;)
When I got into front end, I used redux for a small SPA with about a dozen actions and didn't really get it. "I could do this with react and basic JavaScript. Why am I writing so much extra?" I refactored redux out and it was much better.
I have another application that interfaces with a number of APIs, is a very thick client and needs to recover from local storage on startup. Redux fit like a glove!
I think a "Quick Start" section would solve a lot of the so-called "complexity" issues, showcasing a few common use-cases:
- Without React - With React
Put bare-bones code under each of those sections and then let the user learn the particulars as they come (and as their projects demand)
The core idea of Flux, and the theory behind Redux, is very simple. But I personally dislike Redux.
Having DRY code is not "magic".
@observer seems like the same thing as connect(), but it passes down the entire state as context.
¯\_(ツ)_/¯
Just curious, how can you test a mobx store independently? (Sans components)
The way I have it setup, everything is in a central store, and the components only access it rather than have observables for any kind of local state.
Local state is handled with boring old setState calls, so the store and data-layer is totally independent of React, and can be tested without even importing the library.
> (Look for the constant MAX_REACTION_ITERATIONS)
Okay... that's interesting. It dates back to Feb. 2016th[1], and I'm not caught up with that version. My pull comes from January [2].
Giving it a brief look over, it looks like they changed the codebase to allow for reacts to trigger themselves. My version doesn't allow that. It's a fatal error instantaneously.
Presumably, this was done to let people write recursive functions.... but I'm not 100% sure this is a good idea.
Edit: I think my version implicitly has MAX_REACTION_ITERATIONS = 1, so observables triggering itself is an immediate error.
[1] https://github.com/mobxjs/mobx/commit/a38b6a8c255d772a79dadd...
[2] I know I should update, but I come from the boring Python world where you don't expect the internals of a library to change too drastically year to year.
This "Redux in a nutshell" implementation might illustrate better what I mean: https://gist.github.com/MarcoWorms/30758235f05faec844b8c06ce...
React-Redux does seem to complicate things for no reason. I have just been using plain Redux with React because I didn't want to learn it.
Quick summary:
- Wrap your component tree in <Provider store={store>
- For any component that needs to access data or dispatch actions, use `connect()(MyComponent)` to create a wrapped-up version.
- Write a function known as "mapStateToProps", and pass it as the first argument to `connect()`. It will be called every time an action is dispatched, and given the latest store state. Extract whatever pieces of data this component needs, return them, and they become props for the wrapped component.
Dan Abramov wrote a miniature version of `connect` , which shows the basic approach it uses: https://gist.github.com/gaearon/1d19088790e70ac32ea636c025ba...
Any code that wants to know when an action is dispatched needs to call `store.subscribe(someCallback)`. The `connect()` function generates wrapper components that manage that process for you, use your `mapState` callback to extract data from the store, and only re-render your "real" component when that data has changed (thanks to a _lot_ of optimization work).
Without `connect()`, you have to manage the store subscription process yourself. More code, fewer optimizations.
There are always going to be alternative tools out there at different timelines. Projects get started at various times when certain tools are popular, and people have varying opinions of what the "best" one is. It's better to focus on making a good product because your users don't give a shit if you used Redux or Mobx.
Always tradeoffs, sadly.
Because they've read "No Silver Bullet" and know a specific solution sold as a panacea when they see one.
But it also does nothing much.
Now Relay...
Another point for Redux: it makes it much easier to understand bugs, both locally and remotely with a tool like our LogRocket (https://logrocket.com)
- "Multiple simple components are better than one highly customisable one" -> Single responsibility principle [1]
- "When all you have is two weeks, keep it lean" -> "Make It Work, Make It Right, Make It Fast." [2]
- "embrace the toughest linting rules your team can stand." -> Not really a principle, but that's almost like simulating statically typed language with compile-time errors and warnings.
[1]: https://en.wikipedia.org/wiki/Single_responsibility_principl...
They allow your redux containers to concentrate on selecting state and triggering actions and the async stuff stays out of the way in 'workers'.
Defo worth a look if your redux project has more than a few pages.
Middleware has quickly become the most important redux feature for me.
You can have a bit of both. D3 has a dependency "d3-scale" [0], which is a perfect choice if you'd like D3 to do the math and React to take care of the DOM.
0: https://medium.com/@mbostock/introducing-d3-scale-61980c5154...
I wished ever developer would do that. Too many mobile apps or websites that do very simple stuff (displaying content, simple forms) don't work properly on older phones because of some sophisticated features that no one needs. Many developers seem to think that everyone uses the newest machines and flagship phones with high bandwidth, and forget about everyone else.
Unfortunately the fear of premature optimization is so big for some people that their users have to suffer just because the programmer wanted to have a nicer developer experience..
Once you have system level tests running, then adding some phones or low-end machines is relatively straightforward.
I'm currently employed as a senior .NET consultant and I am seriously considering changing stacks and trying contracting / remote work in order to have more freedom to travel and work from different geolocations amongst other things.
Should I take some time off to learn React? Do I have to position myself as a junior React developer?
We're hiring react developers in Hamburg, Germany :)
Do you have any more information about your company, vacancies and hiring process?
EDIT: a quick search has answered my question- no REMOTE positions I see :). Hamburg is a nice city though!
As the sibling comment said we're also very open to remote working after a certain period of getting to know each other.
I've refrained from adding the "REMOTE" keyword to our hiring posts because it attracts a lot of remote (non-)"talent", and we're being swamped in mails from people who don't fit the stated profile.
We've been burned hard by remote-only employees in the past because it is very hard to keep the whole team on the same page, even more so with programmers inbetween junior and senior who want a lot of autonomy but end up delivering subpar results.
To me that looks like 3 different things - going from .NET to web dev, going from full time to contracting, and learning React. I wasn't .NET exactly, but went from Windows C++ game console dev to web dev, and I'm currently doing React contracting.
I love web dev, and I don't want to go back to C++. That said, contracting takes a lot of learning and is hard to get used to. Many consider not switching everything at once, but focusing on learning on big thing at a time. React is great to learn, but it's early and time will tell if it sticks. There's a ton to learn about web development that is independent of React.
You do make a good point, though.
it is a sad commentary on the state of software development that the build process is the most complicated and headache-inducing part of exploring a new language or framework, but i've found that to be a constant across many languages and many years.
How good is the Flow type checker, vs ESLint or the Google closure compiler?
While I don't disagree with anything the author said, most of the time I feel like ESLint is just a huge nag about white space. It's defaults have a bunch of rules that in my opinion enforce style without improving code safety at all, despite what the intro docs say. Someone I work with read some opinions like this one and got the idea that ESLint represents correct JS standards, and won't allow the white space rules to be relaxed. I'm stuck being forced to put a space after my comment characters, and not being able to line up blocks of repetitive code. It is admittedly nice to not have any white space style arguments. OTOH, I never really had white space arguments before, and the difference in both friendliness and code safety between using ESLint and the Google closure compiler is huge.
- Isolation of "write logic" means you know where to look to see what code is updating a given chunk of state
- Consistent state handling means it's a lot easier to think about what's going on in the application
- The DevTools show you a log of dispatched actions, and enable you to step back and forth between states to see what the UI looked like at each point
- Middleware lets you centralize app-wide logic, such as consistent transformations for AJAX responses
- Redux's constraints (such as wanting state to be serializable) enable a number of other use cases, such as persisting state across page reloads and synchronizing multiple stores remotely.
- There's a rapidly growing ecosystem of addons and utilities for specific use cases (see https://github.com/markerikson/redux-ecosystem-links )
Things like stepping back through state seem honestly to me to be a gimmick not worth the required investment of time and pain.
I also don't understand the "required investment of time and pain" comment. I've got a couple lines in my store configuration that sets up the Redux DevTools extension, and after that, It Just Works (TM). In fact, one of the posts in my "Practical Redux" tutorial series shows how to configure it: http://blog.isquaredsoftware.com/2016/11/practical-redux-par... .
So in that sense it is a very close representation of reality.
The reality is that the state of your application can be (and I'd say should be) perfectly presentable in a giant object.
Redux is not shy about that and doesn't try to hide or escape from it. The importance is in how you manage reads and writes.
Redux only looks complicated because it tries to gracefully catch every possible user error, and has a bit too many layers of functional abstractions. You can implement the whole thing in a few dozen lines of code.
Why actions? Actions give you an explicit API between the global variable manager(the store) and your components. For one-off components that are cognizant of the fact they're part of an "App", this isn't very useful - e.g. if you want the component to insert "foo" instead of "bar" into your global state, you just go and modify it. For reusable components, however, their interface may be locked(used by other parts of your app or even different apps!). Then you need some kind of adapter to turn the "foo" they give you into a "bar" - actions are one such adapter.
Besides that, actions are one part of the "state machine" design that redux implements. Actions enumerate the possible ways your state can change. Is your state meaningful if you change global.foo = "bar"? It might be, or it might not. Actions are a contract between those modifying the state and those consuming the state, that all changes to the state will leave it in a coherent...state! Even MobX, which essentially takes your "global variable" idea and runs with it, advocates for actions.
Having this guarantee is great for debugging. Is the global state mangled? The global state manager code must be broken, since everyone sends actions, and the only one consuming the actions is the manager. You don't have to hunt for a rogue component that's setting global.foo = "bar" instead of global.foo = "baz". Once your global state manager is ironed out, your problems are less "why is my state mangled" and more "why isn't the component sending the expected action".
In theory, this should simplify your debugging a lot - you are not trying to disentangle a complex set of interactions that led to your state being mangled and a component failing from that, but rather you're just looking for the one component that isn't sending its actions properly. Redux's debugging tools try to highlight this, with the action logger and so on.
In practice, how useful this is depends on how you've designed your actions and global state manager. It is entirely possible to write a trivial global state manager and essentially place all your logic in the actions(for an extreme example, imagine a state manager that accepts an action "set" with a "key" and a "value" - you haven't actually gained anything then!).
Why reducers? If you're convinced that a global state manager and actions are a good idea, you are now stuck deciding how exactly to implement this manager. Let's say you have a yet-unwritten global state manager. You know the current global state, and you've just received an action. A reducer is function with the signature (state, action) => newState, essentially implementing a global state manager! Redux goes a step further and asks that reducers are "pure functions" and that the state is immutable and replaced instead of modified. The reasoning here isn't specific to Redux and it's once again related to consistency and debugging.
Do you really need all of this? That's a good question, and I think it really depends on the project. You may have heard people advocating that you don't use any local state and put everything into Redux. This attempts to solve the "reusable component communicating with others" issue, which by the way I don't think Redux solves well(there's tons of 3rd party libraries trying to deal with that). For this specific case, I think "global variable + event emitter" is a decent solution.
What about more complex state interactions? Well, for them I think the concepts Redux introduces are incredibly useful - not having to worry about your state's consistency is a huge help in debugging. Does Redux itself really deliver them 100%? Well, quite often a lot of your state changes will be coupled with side effects, ranging from animations to API calls. Without carefully designing your reducers, the side effects will quickly eat up any benefit - you may be stuck debugging "why doesn't my action creator pass the appropriate action to my trivial reducers". This is a point that Redux doesn't really handle - there's lots of middleware, addons and full-blown libraries trying to deal with the issue(e.g. redux-thunk, redux-saga, and so on).
My personal opinion is that the concepts that Redux tries to introduce are very useful to know and keep in mind when designing your system. Whether you should commit to Redux the library(and its accompanying middlewares and so on) is a different decision. I think I've learned a lot from studying Redux and thinking about the concepts it tries to introduce. However, all the benefits Redux provides depend heavily on the design of your actions and reducers. With a poor design, you are stuck adding more boilerplate for no gains. This is why I wouldn't use it in many of my projects - I don't think I have the experience to design my state management in such a way as to extract significant value from Redux, without being bogged down by the extra boilerplate.
After a lot of evaluation of different js frameworks over the last year I reached the same place with Redux. The principal is sound; it's clean, simple, and incredibly easy to understand. Ultimately though, your logic can end up scattered about the codebase and I worry about what these codebases are going to look like in a year's time.
With MobX you can take a cue from Redux and enumerate all your actions. You're then free to apply the same strictness as you have with Redux in terms of the actions and dataflow.
When people compare Redux to MobX, the thing that is always missed is that MobX provides patterns for efficient calculated / aggregated state. There's no logic in there - it's just a way of transforming your raw state into a read-only structure more suitable for your views.
I guess the situation we've got now is that there are a lot of people who have gone deep on the Redux approach. And really - the code looks like Redux, which is one of my major concerns. When everyone decides that it's hard to manage the sprawling logic of a big Redux app it's going to be very hard to transition to any other pattern.
On the subject of sagas, does anyone have a good solution for dealing with `call` in TypeScript without losing type safety?
1. "Configuration over Convention" seems to be the mantra with build settings and organization of the entire application. This can make adding features slower if you're used to frameworks like Rails.
2. SEO will require additional effort via server side rendering
Hehe.
If nothing else, this makes asking React based interview questions complicated.
That "js stack from scratch" repo is also very helpful in setting up the whole build.
So whenever I start working on a new area, I should add that to my job title?
React Carpenter?
http://www.jasonbock.net/jb/News/Item/7c334037d1a9437d9fa650...
Also:
> 1: Multiple simple components are better than one highly customisable one
No shit. Ever heard of UNIX?
Disclaimer: I'm the author.
You're right, many free projects offer the same or even greater functionality, however, you're not taking into consideration time you spend to put everything together. If your client is paying you hourly, Cx is an investment which pays off quickly. We're contractors too and this was one of the reasons behind Cx.
I understand that commercial licenses are not very popular these days, but for a small company like ours, I don't see any other model allowing us to concentrate on support and the product in the long term.
I also appreciate that writing a good licence is hard, but yours does seem to be particularly awkward. For example, if we're developing a project for a client, our terms typically state that they will have a certain minimum standard of licence for any third party code we use, so they know they're in the clear deploying or distributing whatever we build for them. Your terms giving only a revocable licence would immediately disqualify your library, as would any other hedging about being able to use it indefinitely once it was paid for.
The bottom line is that our clients don't want to get bogged down with the terms of third party contracts or licence agreements and whether they could undermine the whole project, so anything with the slightest hint of risk is immediately more trouble than it's worth in this sort of area.
But your licence scheme seems to favour devs who only work for one company a contractor would get a new one for every project.
I don't understand whether this license is reasonable or not (setting aside price) - which is a problem. Most developers will not hire a lawyer to parse the 7 pages of legalese.