The redux docs state the store is an observable and you can subscribe to it. Does anyone know how to add operators to store subscriptions such as 'throttle'?
import 'rxjs/add/operator/throttle'
this works fine:
store
.subscribe(() => saveStateToLocalStorage(store.getState()))
this doesn't:
store
.throttle(1000)
.subscribe(() => saveStateToLocalStorage(store.getState()))
Indeed, redux store is an observable but it's not based on RxJs.
You can't do that kind of thing natively.
If you're using angular (v2 ou +) with ngrx, your store is an observable built on top of RxJs so you might do something like that.
If you're not using ngrx, you should take a look into this redux-observable : https://github.com/redux-observable/redux-observable
Here is a solution using RxJS 5:
const subject = new Rx.Subject().throttle(1000)
subject.subscribe(() => {
saveStateToLocalStorage(store.getState())
})
store.subscribe(() => {
subject.next()
})
Related
This is a topic that's been discussed a lot through github issues and by now I've noticed two main opinions: It's not possible or it should not be done at all.
The argument for both sides is that redux is not meant for it, that the .replaceReducer function is only meant for the purposes of hot-reloading (even though redux itself mentions it as a possibility for code-splitting).
The goal
Anyway, what I would like to achieve (ideally) is a system that only sends the relevant slices and relevant redux code for a specific route in NextJs. And (even more ideally) when navigating between pages the store should just get extended and not re-created.
My initial approach
My first idea was to implement a recipe from the link above, attaching and exposing the injectReducer function onto my store during the store setup:
const store = configureStore({
reducer: {
globals,
[rtkqApi.reducerPath]: rtkqApi.reducer
},
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(rtkqApi.middleware)
});
store.dynamicReducers = {};
store.injectDynamicReducer = (name, reducer) => {
if (Object.keys(store.dynamicReducers).includes(name)) {
return;
}
store.dynamicReducers[name] = reducer;
store.replaceReducer(
combineReducers({
globals,
[rtkqApi.reducerPath]: rtkqApi.reducer,
...store.dynamicReducers
})
);
};
const makeStore = () => store;
export const wrapper = createWrapper(makeStore);
export const injectReducer = (sliceName, reducer) => store.injectDynamicReducer(sliceName, reducer);
So basically every page would have a globalsSlice, containing the user info and some other global data, and Redux Toolkit Query API slice (which would then be code-split using RTKQ injectEndpoints functionality).
With this setup, each page that wants to inject its own custom slice (reducer) would do something like this:
const SomePage = () => {
const someData = useSelector(somePageSliceSelectors.selectSomeData);
return (
<Fragment>
<Head>
<title>Some Page</title>
</Head>
</Fragment>
)
};
export default SomeRoute;
injectReducer('somePageSlice', somePageReducer);
export const getServerSideProps = wrapper.getServerSideProps((store) => async (context) => {
// Whatever necessary logic we need
});
Initially this seemed to have worked fine, but then when I realized that next-redux-wrapper works by calling the makeStore factory on every request, and I'm manipulating and mutating a global store object, there has to be something wrong with this, ie a race condition that I haven't been able to cause by testing. Also another problem occurres when using Redux Toolkit Query. For example, if I need to get a cookie from the original request (the one that nextjs receives) and then re-send it to another API endpoint that is handled by redux toolkit query, I would need to extract the cookie from the request context, to which I don't have access unless I do something like this:
export const makeStore = (ctx) => {
return configureStore({
reducer: ...,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
thunk: {
extraArgument: ctx,
},
}).concat(...),
});
};
which further implies that I should definitely not be mutating the global store object.
So then I thought alright, instead of manipulating the global store I could try doing it in GSSP:
export const getServerSideProps = wrapper.getServerSideProps((store) => async (context) => {
store.injectDynamicReducer('somePageSlice', somePageReducer);
});
But no luck here, the slice does not get loaded and the state does not get constructed. It is my guess that the Provider in the _app gets rendered before this, but I'm not sure.
In conclusion, I'd like to know whether anyone has tried and succeeded in implementing redux code splitting using RTK, RTKQ and NextJs. Also, I would like to ask an addition question: Is it necessary? What I mean by this is, if I were to not code-split at all, and send all slices on every request, how performance impactful would this be? Also, since I'm not sure exactly how the NextJs bundler works and how code chunking is done: If a certain page receives a slice it doesn't use at all, will it only receive its initial state or all of its logic (all the selectors, reducers and actions)? If not then maybe this isn't so bad, since initial states are just empty objects.
I hope I've presented the problem clearly enough, as it is a very complex problem, but feel free to ask follow up questions if something doesn't make sense.
Thanks in advance.
I am beginner in Redux and I want to use it for asynchronous logic. Redux style quide recommends to use redux-thunk for it, but it seems I don't need it if I use redux in following way:
class Actions {
constructor(dispatch) {
this.dispatch = dispatch;
}
someSyncAction1(data) {
this.dispatch({
type: SOME_SYNC_ACTION1,
payload: data,
})
}
someSyncAction2(data) {
this.dispatch({
type: SOME_SYNC_ACTION2,
payload: data,
})
}
async someAsyncAction(data1, data2) {
this.someSyncAction1(data1);
await somethingAsync();
this.someSyncAction2(data2);
}
}
// then in my react component:
function MyComponent() {
const dispatch = useDispatch();
const actions = new Actions(dispatch);
//...
return <div onClick={() => actions.someAsyncAction(1, 2)}></div>;
}
It seems to be a simple way but I worry whether it can lead to errors. Please help me to understand what is wrong with it.
This is not very different from the useActions hook referred to in the Hooks documentation - at least for the synchronous stuff.
In the async stuff, you are losing functionality though: Thunks can at any given time access the current state by calling getState.
Also, and this is probably more important: thunks are not only recommended, they are a pattern almost every redux developer knows. So they look at your code and can immediately go to work. Your pattern on the other hand is not established, so it will lead to conflicts if someone other will ever take over your code - without any real benefit.
With reduxjs-toolkit I define my store and middleware something like this:
const store = configureStore({
reducer: rootReducer,
middleware: [reduxWebsocketMiddleware, adamBrownMiddleware]
});
However I only want to apply this middleware to certain reducers, not all. Specificially I want to apply websocket middleware to certain reducers, whereas in others I'm calling an API and this websocket middleware is not necessary.
It seems like adding another store to do this is an anti-pattern.
So how can I apply middleware only for certain reducers?
You can always, in your middleware, check the action.type property (for example if it begins with "mySliceName/") and otherwise skip the rest of the middleware.
As you don't really give an example what your middleware is doing and why you want to limit it, it's not really possible to give you any more input than that.
This could look like this:
const middleware = api => next => action => {
if (!action.type.startsWith("myWebsocketSlice/") { return next(action); }
// normal middleware code here
}
I am new to react native with redux. I am trying to figure out how all the pieces in react-native redux integration. The one thing giving me trouble is understanding the difference types and selector give me more details.
MapStateToProps -> has his name say, you can map state objects to props. Example:
You have a store like this:
{
name:'paul',
surname:'watson'
}
Then you need show in your component the name, so in your container you can access to this data stored in store with mapstatetoprops, like this:
const mapStateToProps = (state, ownProps) => ({
myname: state.name,
})
MapDispatchToProps -> thats when you need dispatch an action, you map an action to a prop to you can use in your component
You have an action like:
const setMyName = payload => ({
type: SET_MY_NAME,
payload,
})
then you need update your name in store when user click something throw this action, so you can map this action in a prop to call like updateName('pepito') with mapDispatchToProps, like this:
const mapDispatchToProps = {
updateName: setMyName,
}
Selectors -> it's just an abstraction code, selectors make your life more easy.
Selectors are functions that take Redux state as an argument and return some data to pass to the component, like this:
const getDataType = state => state.editor.dataType;
Thats a basic concepts, you should read oficial document and search, in internet have a lot of articles about this.
I have this question in my head, not sure if this is validate or not, below it's an example of redux middle console log out the store.
const logger = store => next => action => {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
I can see it's using currying, so in redux is calling as logger(store)(store.dispatch)(action) (Correct me if i am wrong). My question is why we currying here instead just
(store, next, action) => { // do the rest }
Thanks for any suggestion I am slowly moving into functional programming too to get my head up rhythm with it.
I think redux wants to provide three hooks to developers.
We can split the call chain logger(store)(next)(action) into
let haveStoreAndDispatch = logger(store);
let haveNext = haveStoreAndDispatch(next);
let haveAction = haveNext(action);
Then we get three hook functions.
In haveStoreAndDispatch callback function, store have been created.
In haveNext callback function, we have get the next middleware.
In HaveAction callback function, we can do something with the previous middleware's result action.
Callbacks haveStoreAndDispatch and haveNext just be called only once in applyMiddleware(...middlewares)(createStore).