At first the most critical choice. For us React was the way to go because it is backed by a huge company and also seems to have great adoption outside of it. There are multiple conferences attended by over a 1000 people each year which shows the enormous potential of the ecosystem.
My favorite thing about React is that step 1 is just "ok first learn the language you're going to build in. Good now you know 80% of React".— jordwalke (@jordwalke) June 29, 2017
Redux or MobX?
I think one of the most important questions when starting to use React is how to handle state. While React is great at rendering different states its internal setState mechanism has some drawbacks. It seems like a pretty easy concept but it still seems to need pretty long articles in order to explain it properly. And it binds the state to a concrete component which means it is not that easy to have a single source of truth. I have also tried to use the internal setState but after a few minutes I already ran into problems like explained in this blog post.
Apart from that it was obvious to us that we would like to have a single source of truth. That's exactly what Redux offers. It has an architecture that reminds me a bit of CQRS which also comes with the same downsides. It is quite hard to work with because for almost everything you do you need to define an action, an action creator and a reducer. So for implementing even simple features you need to touch at least three different files which feels a bit like an overkill to us. The other thing is that even the creator of Redux seems to think that it is overused.
The other state management solution we were aware of and finally favoured over the others is MobX. What I like about it so much is that it has just the right amount of magic. It allows you to define some observable values and let some code automatically run as soon as these values change. That's really nice when being combined with React. The observables are the single source of truth and all of your React components dependant on that value automatically rerender if necessary. There are a few gotchas but overall it still feels like a great choice.
We also can't support the statement that MobX only works for smaller projects. We would consider Sulu 2.0 quite a big project and that's exactly where MobX excels. For smaller codebases it might work to update stuff manually but with larger code bases Mobx really shines because it takes care of everything being dependant on an observable.
Another reason to go for MobX was that it feels easier to extend for different resources and it is closer to what PHP developer are used to. And since our driving force is still the PHP community that is a very important criteria.
I think everybody has seen this one CSS codebase from which everybody is afraid to delete some code because there is no way to find out if the specific statement is being used in some weird situation. That's one of the reasons we decided to use CSS modules. It allows us to give CSS classes a local scope so that the definitions from a certain CSS file only have a very limited scope. It does that by adding a hash to the class names. This way it is impossible to break e.g. the navigation component only because we've adjusted something in the button component (at least as long as we are not using the :global selector).
The building blocks of our architecture
Apart from the previously mentioned tools we are also using babel and webpack to translate and bundle modern ES6 code but that appears to be pretty the standard these days. What's more important is how we have built our architecture.
Components are simple building blocks and have only internal state. They don't take any external state except for props and can be easily reused. You can think about a table, an input field, a date-picker or similar stuff. Components are React components and are only allowed to use other components.
Containers are also allowed to use services which are classes not being dependant on React. Examples for theses classes are the router, which allows us to recognize the value of the URL and act accordingly, or some classes for creating requests.
However, just giving some options to each view is often not enough for the complex information you need to pass to a form or a datagrid. Therefore we also introduced the concept of ResourceMetadata. You can use XML to define different aspects of your entities, similar to what you are already used to for defining templates. This metadata will then be converted to JSON, transferred to the browser and can then be reused to correctly render and validate forms as well as send requests to the server.