React’s simplicity lies in that it makes it easy to declare user-interfaces in self-contained independent components. It hides its complex rendering engine implementation and exposes a simple component API. There are only a few public methods to override inside the component such as componentWillMount, componentDidMount, etc.
In React, every UI is composed of components, so there is no mismatch of multiple types to worry about. Since there is only one common object, it makes it easy to nest components inside components and encourages the principle of single responsibility for each component.
The React UI is declared inside components. A component is like a function that accepts props as inputs and outputs the declared interface. The UI should be composed of as many components as possible to maximize reusability.
If a widget is composed of multiple components, the state should be inside the parent component. Any asynchronous I/O requests shouldn’t be inside the pure component. Doing so would make it less usable as it couples the I/O requests to the component. If you tried to use the component in a large list, it would create a large amount of requests on rendering the components.
Try to avoid using inheritance, and use composition from components. For example, if you need more functionality from a component, make a wrapper component. There are different kinds of components besides just UI components and container components. It is useful to think of these as pages or screens that contain all the components, handle actions to request the data, and also handle local inner component communications.
One-Way Data Flow
React uses one-way data flow, so only changes to the data result in changes to the UI. If you use Redux, you change data in a global app store, and the components listen for changes there. The benefits of having all the app state in one place is having one source of truth. Another benefit is it makes it easier to inspect changes in the app and captures the different states of the app. This helps when trying to reproduce errors.
The Virtual DOM allows the UI to be efficient in diffing UI changes. This in-between process between what the component should render and what the actual rendering is that it allows React to work on different platforms. React Native runs on mobile devices and there are also native OS platforms that run on Ubuntu, Mac, and Windows.
The in-between step is where React actually does most of its work. It does this using the difference between the last declared render and the updated render in a virtual model of the interface, applying to it the real interface implementation. The reason for the virtual DOM is that in browsers, certain changes to the DOM cause browser engines to recalculate the CSS layout, reimplement the layout, and redraw the page. React batches the minimum amount of changes needed in each UI cycle, reducing the redraw frequency.
React isn’t just the UI, it’s a framework/ecosystem. You can manage the state using a state manager such as Redux. This is abstracted away from the component tree. It makes debugging easier to not have state hidden in multiple places. There are good tools following this stack such as Redux DevTools, which allows you to see the app state as a collapsable tree widget.