Can redux be seen as a pub/sub or observer pattern?
Can't upvote Fabios answer twice and the comments section is to small:
Yes in my view it's like a pub/sub with a convention.
To illustrate how passing down props can be a pain, here's an example.
Let's say you have a component that displays the current logged in user. Your components hierarchy is like a huge tree. Now if you would display that component on a branch starting at the root, and in another branch also starting at the root, each deep deep down in the hierarchy, you would have to pass the user infos from the root component to the leaf nodes on 2 different paths, polluting the props of every components in the way (which aren't related at all to user's infos, but now they need to know).
Redux is not supposed to "replace the initial idea of react.js", think of it more like a library to managed shared state between components and to coordinate state mutations.
Redux does use a pub/sub pattern indeed, see the store methods here:
http://redux.js.org/docs/api/Store.html#store-methods
You'll find a subscribe
method that is used by components to subscribe to changes in the state tree. Normally you don't use store.subscribe directly, as the Redux-React bindings (Redux connect
basically) do that for you. You can check out the actual implementation here, it's not that complicated to follow (in fact to me that's the main benefit of Redux over other Flux implementations): https://github.com/reduxjs/react-redux/blob/4.x/src/components/connect.js#L199
That code, apart from subscribing to the changes emitted by the store, also perform some optimisations, such as passing new props to the component (and hence triggering a re-render) only when that's really needed.
Consider also that it's perfectly fine to keep using the components internal state together with Redux. You can use the internal state to store state you don't need/want to share with other components.
You see the need of something like Redux when you have a more complicated application, with top-level components that need to talk to each other (actions) and somehow share some state.
Actions in Redux are by default just POJO (plain old javascript objects), you can think of them as "events" that you often dispatch in response to user-triggered actions (e.g. user clicked on a button), but you're not limited to that, you can dispatch an action from wherever you want. The Redux store listens for these actions and calls the reducers (pure functions) passing the action object you dispatched.
Reducers intercepts all the dispatched actions and they can return a new, updated state for the slice of state they manage.
In this sense, a reducer is a function that processes the actions and updates the state as needed.
In turn, when a reducer updates the state by returning a new copy of the state, connected components (subscribed to the changes in the state) will be passed new props and will re-render to reflect the changes.
Sometimes, dispatching just plain js objects is not enough and you need more control. That becomes clear when you need to perform more complicated logic, for instance when you need to update a counter in the state based on the response from an AJAX call.
With redux-thunk you can dispatch functions as opposed to just plain objects. By dispatching a function, you're effectively implementing the inversion of control pattern in a very simple way. You action becomes a "recipe" described by the function, and not just a simple statement, as with a POJO action.
Why just POJOs supported "out of the box", for actions, why isn't there a base Action class or something? Mainly because there's no need for that. A simple object (considered as a bag for values basically) with a type
property is all you really need, it's basically the simplest possible interface. You can consider this to be programming against an interface (the action) instead of an implementation.
Why a global state is better, instead of each component managing its own state? Mainly because managing the state is actually the hardest part of a non-trivial js app. By using Redux, you extract all that logic from the UI layer, making it easier to test. In fact, you should ideally be able test all the real "business logic" of a Redux app without even rendering a single component.
Components become "dumber" and "purer" as they just render what they are told to do. "Purer" here means that because they don't hold any state, what you see rendered just depends on the inputs (read "props") at any given point in time, and not by any history, hence "stateless".
Having the state as a single, json serialisable object also makes it easy to debug, to snapshot it and send/restore from a server or local storage.