The perfect choice of one-stop service for diversification of architecture.
In this post, I would like to share a brief history of ReactJS and its solutions to state management. You may probably know these already, but I think it would give a glimpse of how React evolves all the way long for those who are new to this framework, or a quick refresh if you are a veteran.Two Camps of ComponentsReact has two types of components Class-based and Functional.
As the names suggest, class components inherit React.Component and implement methods such as constructor and render for React to invoke, while function components are literally functions that take some inputs props and return the component to be rendered.Prior to React v16.
8, only class components can maintain a state and expose a handler to update the state. Whats more, there is a full set of methods to perform state management, side effects, and calculations at different time points throughout the component lifecycle. Class components can do everything you need to render a single-page application, even with performance optimization with thePureComponent, and it is the only way to do these jobs.
Function components, on the other hand, are pure functions that cannot have state and side-effects, but just rendering. One may call them dumb components, in contract to smart components that can contain much more business logic, side-effects, state manipulation, and controls on child components.Why we cant settle with class components?
So it seems class components are powerful enough to solve our daily problems and build all kinds of applications we need. Yet, there are various downsides when using class components:Boilerplate with callingsuper(props) in constructorThe binding issue with this keyword (i. e.
must do this. someMethod = this. someMethod.
bind(this) in constructorwhen declaring class methods, unless declaring it as a class field with array function syntax, which requires babel plugins for transpilation)Spreading business logic and data manipulations across multiple lifecycle methodsReusability of stateful logic state management methods like this.state and this.setState are attached to the class component and very hard to share with another component with similar needsImperative vs.
declarative functional and declarative codes are more suitable for UI presentation with higher readability and testabilityThus, the React community leans to function components, which are essentially just the render method of a class component, dropping other lifecycle methods. Then other tools and patterns come in and patch the missing state management feature for function components, such as Redux brings in the global store and reducer, recompose gives us withState and withHandlers, etc.Most of these libraries use the Higher-Order Component (HOC) pattern which embraces function composition, a very core pattern in functional programming.
This may save us from the downsides of the class components and give full power to the function components, but the wrapper hell issue is the Achilles heel of HOC that makes the virtual dom tree very complex and hurts rendering performance. The official extensions for function componentsAfter all these attempts from the open-sourced communities, the React team finally proposed the official solutions in 2018 the React hooks. Hooks try to solve the same problem, state management in function components, with a different pattern.
As mentioned in the hooks proposal:Hooks let you use more of Reacts features without classes. Function components with hooks end the era of class components, as well as all the 3rd party libraries for state management.If those 3rd party packages like recompose and redux killed class component with HOC, then probably hooks and other React 16 new APIs killed those 3rd parties, by bringing back those features into the official React.
useState can replace withStateuseEffect can replace lifecycleuseMemo and inline functions can replace withHandlersuseReducer can replace the reducers from reduxReact Context API can replace the global store from reduxReact. memo can replace the class-based PureComponent or pure from recomposeThere could still be valid use cases to use redux and redux-saga in large-scale applications, but recompose is completely superseded by hooks.To me, the machinery of hooks is too powerful and it brings to new concepts that I doubted to adopt it in my project.
For instance, it did take me some time to understand the useEffect hook and its dependencies array. I also felt a bit weird to declare side-effects within a render function.After trying it out on some new components on small-scale projects, I found the simplicity and clarity of having state management in a function component.
I no longer need to trace where the props come from, jump around lifecycle methods and multiple layers of HOC to look for business logic, digging into HOC to figure out what props are injected. The development and debugging experience are largely improved, not to mention the rendering performance boost.Its all about Trade-offsOf course, I am not saying class components are gone and we should migrate all components to the functional style.
In fact, the React team has no plans to deprecate class components and it is not suggested to do a big bang factoring to rewrite all your existing components. After all, everything has a trade-off and it is a matter of taste to pick the right pattern that solves the right problem.I hope you now understand more about the evolution of state management and find the best pattern that fits your use case