What is the use of referencing appState/rootState in feature store - ngrx

Trying to understand the ngrx example app, got stuck and unable to figure out its use case.
What is the importance of State in the below code taken from ngrx-example-app
export interface State extends fromRoot.State {
[booksFeatureKey]: BooksState;
}
/** Provide reducer in AoT-compilation happy way */
export function reducers(state: BooksState | undefined, action: Action) {
return combineReducers({
[fromSearch.searchFeatureKey]: fromSearch.reducer,
[fromBooks.booksFeatureKey]: fromBooks.reducer,
[fromCollection.collectionFeatureKey]: fromCollection.reducer,
})(state, action);
}

Doing this has no run-time differences, it's only to make typescript aware you "have" a root state.
This is needed to make typescript happy if you access root state or root selectors from within your feature state selectors.

Related

Handling namespaced modular approach on PINIA, Vue3+Typescript

normally I was using namespaced vuex. But I am deciding to quit vuex because Pinia has the vue core team support. I think it's better for the future developements. Now I am creating store with a modular approach but couldn't really understand how to handle that part on typescript project.
let's say I have a user interface.
interface User {
email: string,
username: string,
}
export default User;
and in store/modules/state.ts I am calling the Type and creating a user state.
import User from "../../types/User"
export const state = () => {
return {
user: {} as User | null,
};
}
and in store/modules/index.ts I should import the state. And make the namespace: true then export it for the defineStore() for pinia store.
import {state} from "./state"
export default {
namespace: true,
state,
}
in store/index.ts
import {defineStore} from "pinia"
import {data} from "./modules"
export const Store = defineStore(data)
okay above, namespace part I use the vuex way. But what is the right approach for the pinia. Additionally, getters and actions as well. How should export and use them.
According to official Pinia docs:
Vuex has the concept of a single store with multiple modules. These modules can optionally be namespaced and even nested within each other. The easiest way to transition that concept to be used with Pinia is that each module you used previously is now a store.
So now you should think about each vuex module as an separated pinia store. Looking at your example it could look like this. create file in store/modules/index.ts and paste:
import { defineStore } from "pinia";
import state from "store/modules/state.ts"; // Assuming that it's path to user state
export const useUserStore = defineStore('some/vuex/module/name', {
state: state,
getters: {
// your getters here, check the Offical Pinia above
},
actions: {
// your actions and mutations here, also check the offical Pinia Docs
}
})
If you want to split getters, actions and state into multiple files, there is discussion on offical repo issue where I provided example, that is working for me. Here is a link

Redux Persist + Redux toolkit $CombinedState error

I'm trying to add redux persist to redux toolkit but for some reason I get an Exported variable 'store' has or is using name '$CombinedState' from external module ".../node_modules/redux/index" but cannot be named. error on vscode.
This is my store configuration file with the added persist config, which if I remove, works fine.
import { configureStore } from "#reduxjs/toolkit";
import { persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";
import { createEpicMiddleware } from "redux-observable";
import rootEpic from "onsite/redux/rootEpic";
import rootReducer from "onsite/redux/rootReducer";
const epicMiddleware = createEpicMiddleware();
const persistConfig = {
key: "root",
storage: storage,
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
// Line that shows error
const store = configureStore({
reducer: persistedReducer,
middleware: [epicMiddleware],
});
export default store;
epicMiddleware.run(rootEpic);
Other things I have tried are putting the combineReducers declaration (from rootReducerimport) into the same file or converting the file into plain javascript, with same or similar results. For some reason typescript decides to still haunt me on a javascript file :)
The code still runs, so I'm tempted to leave it like that, but I would like to get rid of it.
I had the same issue, and here's what I figured out:
You need to include Store from redux, and use it as your type definition for your own store return value. Short answer:
import { Store } from 'redux';
[...]
const store:Store = configureStore([...])
[...]
export default store;
Longer answer:
As I understand it, what was happening is that Store type uses $CombinedState as part of its definition. When configureStore() returns, it inherits the State type. However since State is not explicitly used in your code, that now means that your code includes a value that references $CombinedState, despite it not existing anywhere in your code either. When you then try to export it out of your module, you're exporting a value with a type that doesn't exist within your module, and so you get an error.
You can import State from redux (which will in turn explicity cause your code to bring in $CombinedState), then use it to explicitly define store that gets assigned the return of configureStore(). You should then no longer be exporting unknown named types.
You can also be more specific with your Store type, as it is a generic:
const store:Store<RootState>
Although I'm not entirely sure if that would be a circular dependency, since your RootState depends on store.
Adding a
import { $CombinedState } from '#reduxjs/toolkit'
in that file should usually resolve that error.

Why is immer complaining about computed properties?

So based on a recent answer, I've started using ReduxStarterKit, which uses Immer in it's slice function. Overall, I think the basic idea is brilliant.
Unfortunately, when I try to actually make use of immer to make my life easier, I run into issues. I've tried to simplify what I'm doing using my test reducer, and I'm still getting the same basic problem. I've also turned off all my middleware (ElectronRedux) to make sure it's not a problem there. The following is a simplified test reducer I'm using for testing:
const CounterSlice = createSlice({
name: 'counter',
reducers: {
increment: (state)=>{state.value = state.value + 1},
decrement: (state)=>{state.value = state.value - 1}
},
initialState: { value: 0 },
})
The code above is pretty simple, and as far as I can tell exactly what Immer/ReduxStarterKit wants me to write. Despite this, when I call the code I'm getting an error: Uncaught Error: Immer drafts cannot have computed properties
What am I doing wrong?
Edit:
I just put together a simple demo app, just for the purposes of testing the basics. The counter slice has coded here works perfectly. I guess this is an interaction between Immer and another package -- just not sure where to go about debugging which one. Is it a redux version issue, is it electron, electron redux, typescript, webpack, the list goes on and (painfully) on.
I may have to recreate my basic app environment and test this one painful step at a time. Ugh!
Turns out the problem wasn't in the code I was sharing, it was way over to the side when I was setting up my initial state. Electron was manipulating the data I sent back and forth via getState(), adding getter & setter methods. Those getter & setter methods were (quite rightly) triggering this error.
const initialState = remote.getGlobal('state');
console.log("initial state: ", initialState);
const store = CreateStore({initialState:{...initialState}, main: false})
Expected log output: { counter: { value: 1 } ...rest}, but actual output was { counter: {value: 1, getValue: function(), setValue: function() }, getCounter: function(), setCounter: function(), ...rest.
Woops. Now I just need to 'clean' my state up, since apparently state storage somehow persists this failure. And then figure out how to strip the (nested!) getters/setters. Ugh.
Edit: Rather than stripping the getters / setters, I just needed to use the built-in getInitialStateRenderer from electron-redux. So change the above code to:
const initialState = getInitiateStateRenderer()
const store = CreateStore({initialState: initialState, main: false})

Redux. How to run several sagas in sagaMiddleWare.run([ f1*(), f2*(), f3*() ]) with redux-saga?

I need the solution to start several sagas in different time points. I had created 3 different saga's and tryed to implement they to the sagaMiddleWare.run() like an array. But for now I gets an error - runSaga(storeInterface, saga, ...args): saga argument must be a Generator function!.
I understand why this error happens, but does not understand how to solve it?
Thank you!
sagaMiddleWare.run([watchSearchForCash, watchBootlegging, watchGraffiti])
You can make it work easly by combining all sagas in one wrapped, called (for example) rootSaga:
function * rootSaga() {
yield [
watchSearchForCash,
watchBootlegging,
watchGraffiti
]
}
and then implement your new saga holder rootSaga inside the sagaMiddleWare.run(rootSaga)
Thant's all the magic :)
all watchers must have () because all are functions !
import { all } from 'redux-saga/effects'
import productSaga from './product/saga';
function* rootSaga() {
yield all([
watcheProductSaga(),
watcheUserSaga(),
])
}
export default rootSaga;

Cant create connected containers or enhancers for griddle

I'm trying to create an Enhancer for griddle v1.6.0.
I am getting this error:
Uncaught Error: Could not find "store" in either the context or props
of "Connect(Row)". Either wrap the root component in a , or
explicitly pass "store" as a prop to "Connect(Row)".
Here is my Enhancer
import { connect } from 'react-redux';
import { selectors } from 'griddle-react';
const { rowDataSelector } = selectors;
export default connect((state, ownProps) => ({
hello: 'world!',
RDZ: rowDataSelector(state, ownProps)
}));
I have a similar problem if I try to create my own Container
EDIT
It appears that the connect is actually connecting to my app's store (which I haven't added a provider for) instead of griddle's internal store. I'm not sure how to access griddle's internal store in an enhancer, or container... Maybe I am missing something here, should I be adding selectors instead?
This a current limitation/bug with Griddle (I think).
I worked around this by passing by data from the app redux to simple component, then that component hands the data to Griddle.
This is not ideal, but works for now.
This discussion maybe of some help.
https://github.com/GriddleGriddle/Griddle/issues/647

Resources