Redux source directory structuring in Javascript - redux

I am exploring a simple redux counter example (using plain npm Javascript standalone project).
Thinking in terms of better code maintenance in medium and large applications , I looking to find-out pros and cons b/w below two styles
Style-1 : Creating common folders at top level dirs like below :-
some-react-proj :
src/ :
store/:
actions/:
counterActions.js
messageActions.js
index.js
actionCreators/:
counterActionCreator.js
messageActionCreator.js
index.js
reducers/:
counterReducer.js
messageReducer.js
index.js
Style-2 : Separating folders by functionality, like :-
some-react-proj :
src/ :
store/:
counter/:
counterActions.js
counterActionCreator.js
counterReducer.js
index.js
message/:
messageReducer.js
messageActions.js
messageActionCreator.js
index.js

The second one is closer to the current recommendations, but the current recommendations go even further and have you place all of that in one file, and that file next to related components.
The official recommendation is to place reducer, selectors and action creators that belong together into one "feature slice" file, next to components files for that feature into a "feature folder": Redux Style Guide: Structure Files as Feature Folders with Single-File Logic
Obviously sometimes you will have actions that might not really only belong to ONE reducer or feature - you can place those with the feature that belongs to them "the most", or just keep them separated as you want.
For an example of that file structure, you can also look at the file structures provided by the official Redux "Create React App" templates by calling
npx create-react-app my-app --template redux or npx create-react-app my-app --template redux-typescript
Also, this approach goes well with the createSlice function provided by the official redux toolkit that (among other things, like removing the need to write immutable updates by hand) automatically creates action creators for you for "case reducers" you declare and makes stuff like action string types obsolete.
Generally I can highly recommend you to look into modern redux and maybe go through the official Redux "essentials" tutorial that shows how to use modern Redux in practice.

Related

Modularize a Redux Toolkit application

The Question
Is it possible to separate out the feature of an RTK-based application that depend on different slices of a the redux store into separate node packages? Assuming so, what is the best way to do that?
Background
We have a large, and growing, app that is based around Redux Toolkit. Where possible we try to separate parts of the application into their own node packages. We find there are a lot of benefits to doing this, including:
Maintainability of codebase
Fine-grained control over intra-application dependencies
Testability
It's easy enough to do this for cross-cutting things, like logging, http requests, routing, etc. But we would like to go further and modularize the "features" of our app. For example, have the "address book" feature of our application live in a different module than, say, the "messages" feature, with them all composed together via an "app" package.
The benefits we see here are ones we have found in other codebases and have been discussed in other places. (E.g., here for iOS). But, in brief: (1) you can see and control intra-app dependencies. For example, you can easily see if the "messages" feature depends on the "address book" feature and make explicit decisions about how you will expose the one feature to the other via what you export; (2) you can build fully testable sub-parts of the app by simply having a "preview" package that only composes in the things you want to test, e.g., you could have a "contact app" package that only depends on the "contact" feature for building and testing just that; (3) you can speed up CI/CD times by not needing to compile (TS/babel), pack/minify, and unit test every part; (4) you can utilize various analytics tools to get more fine-grained pictures of how each feature is developing.
There may well be other ways to achieve these things, and some may disagree with the premise that this is a good way to do it. That's not the focus of the question, but I'm open to the possibility it may be the best answer (e.g., some one with significant Redux experience may explain why this is a bad idea).
The Problem
We've struggled to come up with a good way to do this with Redux Toolkit. The problem seems to boil down to -- is there a good way to modularize (via separate node packages) the various "slices" used in RTK? (This may apply to other Redux implementations but we are heavily invested in RTK).
It's easy enough to have a package that exports the various items that will be used by the redux store, i.e., the slice state, action creators, async thunks, and selectors. And RTK will then compose those very nicely in the higher-level app. In other words, you can easily have an "app" package that holds the store, and then a "contacts" package that exports the "contacts" slice, with its attendant actions, thunks, selectors, etc.
The problem comes if you also want the components and hooks that use that portion of slice to live in the same package as the slice, e.g., in the "contacts" package. Those components/hooks will need access to the global dispatch and the global useSelector hook to really work, but that only exists in the "app" component, i.e., the feature that composes together the various feature packages.
Possibilities Considered
We could export the global dispatch and useSelector from the "higher" level "app" package, but then our sub-components now depend on the higher level packages. That means we can no longer build alternate higher level packages that compose different arrangements of sub packages.
We could use separate stores. This has been discussed in the past regarding Redux and has been discouraged, although there is some suggestion it might be OK if you are trying to achieve modularization. These discussions are also somewhat old.
The Question (Again)
Is it possible to separate out the feature of an RTK-based application that depend on different slices of a the redux store into separate node packages? Assuming so, what is the best way to do that?
While I'm primarily interested if if/how this can be done in RTK, I'd also be interested in answers--especially from folks with experience with RTK/redux on large apps--as to whether this is Bad Idea and what other approaches are taken to achieve the benefits of modularization.
This question has come up in other contexts, most notably how to write selector functions that need to know where a given slice's state is attached to the root state object. Randy Coulman had an excellent and insightful series of blog posts on that topic back in 2016 and a follow-up post in 2018 that cover several related aspects - see Solving Circular Dependencies in Modular Redux for that post and links to the prior ones.
My general thought here is that you'd need to have these modules provide some method that allows injecting the root dispatch or asking the module for its provided pieces, and then wires those together at the app level. I haven't had to deal with any of this myself, but I agree it's probably one of the weaker aspects of using Redux due to the architectural aspects.
For some related prior art, you might want to look at these libraries:
https://github.com/ioof-holdings/redux-dynostore (deprecated / unmaintained, but relevant)
https://github.com/microsoft/redux-dynamic-modules (also may be unmaintained at this point - still seems to rely on React-Redux v5)
https://github.com/fostyfost/redux-eggs (brand new - the author just posted this on the RTK "Discussions" section recently)
Might also be worth filing this same question over in the RTK "Discussions" area as well so we can talk about it further.
Following #markerikson's suggestion, here's the solution I've come up with. The basic idea involves a dependency injection model where the higher level 'app' package:
imports the 'feature' package's slice
composes a store with it
calls an initialize function also exported from the 'feature' package which injects the dispatch and state (actually the hooks that wrap them, but you could do it either way).
The last part is what allows the 'feature' package to stay un-coupled from the 'app' package.
The 'feature' package also has some typing that defines root state and app dispatch interfaces as types that include at least the local state and dispatch of that feature.
Here's the key code, using the redux-typescript template in create-react-app as a starting point and extracting the counter feature into a separate package. The code is in the counterSlice module
// RootStateInterface is defined as including at least this slice and any other slices that
// might be added by a calling package
type RootStateInterface = { counter: CounterState } & Record<string, any>;
// A version of AppThunk that uses the RootStateInterface just defined
type AppThunkInterface<ReturnType = void> = ThunkAction<
ReturnType,
RootStateInterface,
unknown,
Action<string>
>;
// A version of use selector that includes the RootStateInterface we just defined
export let useSliceSelector: TypedUseSelectorHook<RootStateInterface> =
useSelector;
// This function would configure a "local" store if called, but currently it is
// not called, and is just used for type inference.
const configureLocalStore = () =>
configureStore({
reducer: { counter: counterSlice.reducer },
});
// Infer the type of the dispatch that would be needed for a store that consisted of
// just this slice
type SliceDispatch = ReturnType<typeof configureLocalStore>["dispatch"];
// AppDispatchInterface is defined as including at least this slices "local" dispatch and
// the dispatch of any slices that might be added by the calling package.
type AppDispatchInterface = SliceDispatch & ThunkDispatch<any, any, any>;
export let useSliceDispatch = () => useDispatch<AppDispatchInterface>();
// Allows initializing of this package by a calling package with the "global"
// dispatch and selector hooks of that package, provided they satisfy this packages
// state and dispatch interfaces--which they will if the imported this package and
// used it to compose their store.
export const initializeSlicePackage = (
useAppDispatch: typeof useSliceDispatch,
useAppSelector: typeof useSliceSelector
) => {
useSliceDispatch = useAppDispatch;
useSliceSelector = useAppSelector;
};
A working example of this solution is available in this rush repository.

Can (nx) feature libraries access the global state?

According to the Nx Docs, features of an application should still be moved into libraries. Of course, I can add feature-level state to each of these libraries, but what if there is a property on the global AppState that I would like to access from a feature library? I cannot import anything from the apps/ directory.
Is there a way to share state with feature libraries or should I move my features back into the apps/ directory?
The thing is that there are feature reducers and selectors.
However, it is important to understand that there is no feature store.
There is only one global store which has state for all the reducers.
Therefore feature reducers and selectors work with the global store and global state and can fully access it.
For example, if two different modules call StoreModule.forFeature('test', testReducer) - both modules share the state, although it might give an impression that every module has own test feature state.
My workaround has been to avoid a global store state. Instead, I create a dedicated, componentless module defining the forFeature() state, selectors, reducers, effects, etc. I then export all those from the dedicated module using forRoot().
Now I can import this dedicated feature state module into independent application feature modules. As #satanTime points out, this state is not duplicated or replaced since it is added to the global state via forFeature().

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.

How should I structure my modules in order to make use of hbsfy and browserify?

I would like to use browserify and the hbsfy Handlebars transform to modularise the JavaScript in a web application I'm writing.
Using gulp, I set up tasks to run browserify and hbsfy to compile several js and hbs files into one using a source structure that grouped my modules:
/src
- /javascript
- app.js
- /module-one
- module-one.js
- module-one.hbs
- /moduleTwo
- module-two.js
- module-two.hbs
However this is problematic when referencing one module from another - requiring the use of brittle relative paths that would need updating if a module is moved or further nested.
substack wrote on Avoiding ../../../../../../.. in his (great) browserify handbook. There, he suggests storing app modules in a directory under node_modules.
Happily I set about doing this, but quickly ran into issues with hbsfy no longer compiling my templates. According to the docs, browserify will only transform top level files, and will only transform those in node_modules if the global option is set to true.
This fixed my problem, but the documentation states:
Use global transforms cautiously and sparingly, since most of the time
an ordinary transform will suffice.
I don't believe my use case is unusual - should I be storing my modules in a different manner? Or am I missing a piece of the puzzle?
I found a simple example in hbsfy itself which might be helpful to you https://github.com/epeli/node-hbsfy/blob/master/example/index.js
It depends on what is the framework you are looking at, if you are doing angularjs, you can see an example in https://github.com/mallim/sbangular/blob/master/src/main/resources/js/login/LoginCtrl.js
In this case, the specific code will be as follows:
app.run(['$templateCache', function($templateCache) {
$templateCache.put('login.html', require('./login.html') );
}]);
login.html is actually in the same folder as LoginCtrl.js

Symfony 2 stubs?

I'm seeing some of SF2 components have a folder named stubs placed inside the Resources/ folder. I wonder what is it for? And for my bundle I need to define some global functions, would the stubs folder be a good location to place the files containing these functions?
It seems there are a few meanings for stub. The most relevant I could find was one that described a stub as 'code that is used to stand in for some other programming functionality'.
I.e. acting as a substitute for code that is yet to be developed, or to simulate the behaviour of existing code that isn't usable (or viably usable) under certain circumstances, for example, in a development environment.

Resources