Redux specifies a unidirectional data flow with a single source of truth. And then it gives you very detailed tooling with which to debug this, and an ecosystem of middlewares to augment the incredibly basic/boilerplatey API either to simplify logic or to add extra features (for example, analytics).
I can not speak for every case, so I will talk about why I sometimes use it in React:
First off, there is a synergistic effect where React handles data coming from above in the hierarchy, which Redux facilitates.
Secondly, it can be very hard to keep track of what components are handling what logic in large applications so having it abstracted away in an easy to test way is considered a win for many.
That is the good overview I feel, there's also that popular Dan Abramov medium post[0] that deals with it. I do believe redux is overused, and people would benefit from weighing whether simple react is good enough for a project, buy I understand the risk you feel you take on if the scope runs away and suddenly you need to do a costly migration to redux later on in the project where it can be a lot harder to do so.
[0] https://medium.com/@dan_abramov/you-might-not-need-redux-be4...
"I have to say that I got interested in redux because of all that buzz this framework generated. However, I got really interested in it when I understood how close its philosophy was to functional programming - this, when combined with the usage of react functional components will enable you to write great code and (most important for me) really enjoy coding with it [...]"
So, for me the reason for using redux is its functional programming related philosophy. Ofcourse there are other js frameworks that are more functional (in the fp meaning of the word functional) like hyperapp, choo and definitely many others I'm not aware of but most of them built on the concepts of react functional components and redux, and redux is much more popular.
First, "pure functions" are generally agreed to be easier to test and easier to understand, because they only rely on their inputs, and don't modify anything outside of the function. In a real application of any kind, you realistically can't write the entire app as pure functions. But, writing more of your codebase as pure functions means more of it is easily testable and understandable overall.
Second, while the OOP vs FP debate is never-ending, FP does lead to some nice forms of reusability via composition.
With Redux, you are intended to write your "reducers" as pure functions. It's up to you whether those reducers have complex logic or simply return the values they were given in the actions, but the reducers (which control the actual state updates) should all be pure, and therefore easily testable and understandable. Reducers can also be composed together to add new behaviors, such as:
const finalUsersReducer = undoable(resettable(originalUsersReducer));
In addition, writing pure functions with no side effects, in conjunction with dispatching plain object actions, is what makes Redux's time travel debugging feasible. (That's not to say it's impossible to implement time travel in other ways, just that those aspects of Redux's design make it very straightforward to implement time travel.)Like other state management solutions, it also 'writes' all your shouldComponentUpdate methods for you.