How can I subscribe to changes in a redux store within an actions file - redux

I have the following code
const reduxStore = require('redux/store');
console.log(typeof reduxStore.subscribe);
If I run that in a redux actions file my typeof is undefined.
If I run it in a vanilla js my typeof is function.
Now maybe doing this seems counterintuitive but I have a pattern where I have a shared actions file, that is to say the actions defined in this file can be used in two different reducers.
However some of these actions while shared between reducers (reducer1, reducer2) must cause actions to run that are individual to the particular store domain.
So dependant on what happens when reducer1.acceptGeoCode runs in the part of the application using reducer2 then reducer2.geoCodeSuccess should be run or reducer2.geoCodeFailed
it seems easiest to me to subscribe to changes in my reducer2 actions js, and then on the changes dispatch the local action I should use.
So that is why I want to use reduxStore.subscribe within an actions file. How do I do that, or failing that what is the proper way to do what I want to do which I feel must be doable.

If I understand you correctly, you want to have reducer 2 be aware of how reducer1 handled an action, is that right?
The way to do that is by composing reducers (as opposed to combining them). See this in action here: Stack Overflow
A couple of other points that might help you on your way:
Reducers should be pure. This means we don't do ajax calls (or other side effects) from inside a reducer. That is the domain of actions (ex: redux-thunk) and middlewares.
In the majority of applications, there should really only be one store. So, if you have a pattern where you have one store subscribing to another, then you should probably refactor your code to have just one store. Just to be clear, you can have several reducers combined (or composed) together in a single store - that's how Redux is designed to work.

Related

How to catch invalid action types in redux

I want to be notified if I forget to handle an action type in my reducers. I could make a reducer with a whole list of action types to check against, but that doesn't stop me from forgetting to modify reducers in the first place.
Edit: I am using multiple reducers with combineReducers.
There will always be at least one unhandled action type: INIT. In dev mode, redux will dispatch an action with a random type and also outside of dev mode, you should not act on that action (that is the reason it's random in dev mode).
Really: it is okay to dispatch actions that are not handled by reducers (yet). Actions should describe what is going on in your application, not necessarily be thought of "calling a change in state".
But adding to that: If you write modern redux, it is also pretty uncommon that this happens by accident. You might want to look into modern redux with redux toolkit - the last link is a good start and the official redux essentials tutorial would be a good place to follow up on modern redux patterns.
Also take a look at the redux style guide.
Yes that's is possible. You can throw an error if no action type is matched.
Use this at the end of the reducer function. (but Don't set default type)
throw new Error("Action not matched");

Redux creating new objects on ui

Creating an application that uses redux state quite seriously.
While passing values between the UI components and the reducers. The immutability has become a problem.
1) The cycle problem - I have forms that create JSON objects - which are then used by reducers to maintain the model. The next time you use a form, it is set to the earlier state,.. and either gives error or updates the previous attribute along with itself. Is there someway to capture things in non redux state and then pass it to the states for process?
2) Redux returns or reducers become extremely unmanageable on scale. I have started using immer for this. Is this a good choice?
Are you using Reactjs for creating the forms?
To benefit most from immutabilitiy it is best if your design embraces it also.
What I mean: Develop a form which just displays your data. (a pure form, or FormPure)
Then wrap this form / component by another component which holds the state of the form to be displayed and actually displays the data through the pure form. And allows you to just edit it.
The hierarchy would look like: App > Form > FormPure
Components
Form:
Connected to Redux
May have its own state.
FormPure:
Does not know about redux
Only displays data provided from its parent (in react via props and communicated via callbacks up)
When the user hits the Save Button then the connected Component (Form) may dispatch an action to update the state in redux.
This way you may build your FormPure in isolation separately from any data, and just bring it to live as needed.
You may consider using Storybook for nicely designing your components.
For 2: Yes immer is reasonably fine to use, just make sure you are not overusing it by creating to large object trees, as described in the documentation.
Also make sure to turn off autofreezing when deploying it to production.
Hint: immer uses Proxy make sure your target platform supports them, or make sure to switch then to the ES5 implementation which is considerably slower (see, immer docs)

With React / Redux, is there any reason not to program the store globally?

I love Redux, but to use it, I have LOTS of additional code scattered all over my application: connect, mapDispatchToProps, mapStateToProps etc etc
It appears to me however that I should be able to both dispatch to the store and get any value from the store via a global window level reference to the store object. If I did this, it would cut alot of code out of my application.
So the question is, what is wrong with this approach? Why NOT do all my Redux disptach and state access via window.store?
I wrote a long Reddit comment a while back about why you should use the React-Redux library instead of writing store code by hand.
Quoting the main part of that answer:
First, while you can manually write the code to subscribe to the Redux store in your React components, there's absolutely no reason to write that code yourself. The wrapper components generated by React-Redux's connect function already have that store subscription logic taken care of for you.
Second, connect does a lot of work to ensure that your actual components only re-render when they actually need to. That includes lots of memoization work, and comparisons against the props from the parent component and the values returned by your mapStateToProps function for that component. By not using connect, you're giving up all those performance improvements, and your components will be unnecessarily re-rendering all the time.
Third, by only connecting your top-level component, you are also causing the rest of your app to re-render unnecessarily. The best performance pattern is to connect lots of components in your app, with each connected component only extracting the pieces of data it actually needs via mapStateToProps. That way, if any other data changes, that component won't re-render.
Fourth, you're manually importing the store into your components, and directly coupling them together, thus making it harder to test the components. I personally try to keep my components "unaware" of Redux. They never reference props.dispatch, but rather call pre-bound action creators like this.props.someFunction(). The component doesn't "know" that it's a Redux action creator - that function could be a callback from a parent component, a bound-up Redux action creator, or a mock function in a test, thus making the component more reusable and testable.
And finally, the vast majority of apps built using React and Redux use the React-Redux library. It's the official way to bind the two together, and doing anything else will just confuse other developers looking at your project.
Also, per the Redux FAQ entry on importing the store directly:
While you can reference your store instance by importing it directly, this is not a recommended pattern in Redux. If you create a store instance and export it from a module, it will become a singleton. This means it will be harder to isolate a Redux app as a component of a larger app, if this is ever necessary, or to enable server rendering, because on the server you want to create separate store instances for every request.
Summarizing all that:
Better performance
Lower coupling via dependency injection of the store
Better testability
Better architecture
I'd also suggest you read through my two-part post The Tao of Redux, Part 1 - Implementation and Intent, and The Tao of Redux, Part 2 - Practice and Philosophy. These two posts discuss the history and intent behind Redux's design, how it's meant to be used, why common usage patterns exist, and other ways that people may use Redux.

Redux, I can modify the state - it is not read only, outside of an action dispatch?

Perhaps someone can shine some light here for me.
I have been doing redux, and just was reading about and I read "State is read only". Ok, I figured that was the case - but let me try.
So, in my component I wrap it in a connect and what have you and now I have access to the state... so, I did this, in the render method.
this.props.state.MYSTORE = {}
Well, that actually killed my store. I get the concept of pure functions, but this idea that the 'state is read only' is not entirely true. I figured it would kill just this instance of the store (passed into my component), but the actual store is kaboomed!! I'm a little confused with "state is read only" - clearly, it is not.
What is actually read only? I get that you set state via action creators, but that doesn't forbide bad practice. Just like in a regular component you use "setState", BUT you could just make an assignment on the state tree...
Much of Redux's behavior is expectations and convention over an absolute enforcement. If you're using plain objects for your Redux state, technically you can mutate it directly if you want (but it's almost always a very bad idea!).
Beyond that, Redux is primarily a way to organize all the "write" logic for your application into a single structured approach, rather than having random writes scattered throughout your codebase.
If you do want additional assistance to make sure you don't ever actually mutate your state accidentally, there's several tools you can use. The DevTools#Linting page of my Redux addons catalog lists some plugins that will warn you if you accidentally mutated the state, although those obviously should only be used in development. You can also use a specific immutable data library to either "freeze" your plain objects or give you a specialized set of data structures that wrap up your state contents.

Static functions or Events in Flex?

I'm working with an application which was originally designed to make heavy use of static-variables and functions to impose singleton-style access to objects. I've been utilizing Parsley to break apart some of the coupling in this project, and I'd like to start chiseling away at the use of static functions.
I will explain the basic architecture first, and then I'll ask my questions.
Within this application exists a static utility which sends/receives HTTP requests. When a component wishes to request data via HTTP, it invokes a static function in this class:
Utility.fetchUrl(url, parameters, successHandler, errorHandler);
This function calls another static function in a tracking component which monitors how long requests take, how many are sent, etc. The invocation looks very similar in the utility class:
public static function fetchUrl( ... ):void {
Tracker.startTracking(url, new Date());
...
Tracker.stopTracking(url, new Date());
}
Any components in this application wishing to dispatch an HTTP request must do so via the web utility class. This creates quite a bit of coupling between this class and other components, and is just one example of several where such reliance on static functions exists. This causes problems when we're extending and refactoring the system: I would like to decouple this using events.
Ultimately, I'd like each component to instantiate a custom event which is dispatched to a high-level framework. From there, the framework itself would relay the event to the correct location. In other words, those components which need to perform an HTTP request would dispatch an event like this:
dispatchEvent(new WebRequestEvent(url, parameters, successHandler, errorHandler));
From there, Parsley (or another framework) would make sure the event is sent to the correct location which could handle the functionality and perform whatever is necessary. This would also allow me a stepping-stone to introducing a more compartmentalized MVC architecture, where web request results are handled by models, injected by the framework into their own respective views.
I'd like to do the same with the tracking functionality.
Are there drawbacks from using an event-based mechanism, coupled with a framework like Parsley? Is it better to stick with static functions/variables and use the singleton-style access? If so, why? Will this end up causing more trouble in the future than it's worth?
So, short answer on Events drawbacks:
Slightly more weight on the system to use the events. Listeners, bubbling, capture, etc.. all have to be managed by the system. Much less of an issue when you're outside the display hierarchy, but still more expensive than straight functions. (then again, avoid pre-optimization, so get it right, then get it fast).
"Soft" circular dependencies can occur in complicated asynchronous systems. This means you end up with a case where A triggers an event. B notices A has changed, so updates C. C triggers an event. D notices C has changed and updates A. Doesn't usually max the CPU, but is still a non-terminating loop.
Sometimes you need to have forced buffering / locking of functions. Say component A and B both trigger the same event. You might not want C to be triggered twice (e.g., fetching new data from server) so you have to make sure C is marked as "busy" or something.
From personal experience, I haven't seen any other issues with event systems. I'm using the PureMVC framework in a relatively complicated system without issue.
Good luck.

Resources