I've got LOTS of Reducer Queries that I need to Link up to behavior subjects. At the moment I'm doing this, for every single one!
Is there a more concise way to express this?
this._higherCurveObservable = this.store.let(getCurveSummaryForInstrumentTimeframe(this._instrument, this._timeframe.Code));
this._higherCurveSubject = <BehaviorSubject<CurveSummary>>new BehaviorSubject(null).distinctUntilChanged();
this._higherCurveSubscription = this._higherCurveObservable.subscribe(x => this._higherCurveSubject.next(x));
this._higherCurveSubject.subscribe(x => this.higherCurveChange(x));
There should be no need to create seperate BehaviourSubjects, you could just base your required Observables on the store-stream.
(The only time when you would need something like this - and I'm not assuming you did this - would be the case when you are trying to abuse a BehaviourSubject to be able to take data from the store as well as to dispatch data from the side, in which case you should rethink the architecture, since this would go against the concepts of ngrx of having a single central store.)
So there are multiple ways to write this with less overhead.
Example 1: A perpetual subscription, that never unsubscribes (e.g. in a Service)
this.store
.let(getCurveSummaryForInstrumentTimeframe(this._instrument, this._timeframe.Code))
.distinctUntilChanged()
.do(x => this.higherCurveChange(x))
.subscribe();
Example 2: A temporary subscriptions, that should unsubscribe at a specific point in time (e.g. in a Component, it should unsubscribe when the Component is destroyed)
const higherCurve$: Observable<CurveSummary> = this.store
.let(getCurveSummaryForInstrumentTimeframe(this._instrument, this._timeframe.Code))
.distinctUntilChanged();
this._higherCurveSubscription = higherCurve$.subscribe(x => this.higherCurveChange(x));
// ... some time later
this._higherCurveSubscription.unsubscribe();
Related
Lets say i have a subject that represents pages in a paginateable table:
pageCount$
I then pipe that into a new variable which goes of to an API to get the data for the table:
const tableData$ = pageCount$.pipe(switchMap(pageCount => getTableData(pageCount)));
This works great, every time i emit a new page tableData$ emits the data for that page.
Now here comes the problem and the question i wish to solve.
On my page i also wish to use that table data to display averages of what it currently contains. So i thought i could just pipe tableData$ through a map that performs those averages.
const averageData$ = tableData$.pipe(map(data => performAverages(data)));
This works, but because every-time i subscribe to each variable it creates a new instance my API call happens twice. Once for the tableData$ subscription and once for the averageData$ subscription. I understand this behavior is by design however.
It feels like i want to use some sort of tap/fork operator but i don't think such an operator exists.
Is it even possible to perform these tasks whilst only making the api call once?
Thanks
You can use the share operator to achieve this.
First create the observable that calls the API, and pipe it with share
https://www.learnrxjs.io/learn-rxjs/operators/multicasting/share.
Then the resulting observable can be subscribed twice, both subscription will receive the same results, without the shared observable being called twice ('multicasting').
That should give you something along the lines of :
const tableData$ = pageCount$.pipe(
switchMap(pageCount => getTableData(pageCount)),
tap(_ => console.log('API called')),
share()
);
// subscribe to tabledata$ twice
tableData$.subscribe(_ => console.log('get and use data once'));
tableData$.subscribe(_ => console.log('get and use data a second time'));
(to be tested, feedback appreciated!)
What about something like this
const tableData$ = pageCount$.pipe(
switchMap(pageCount => getTableData(pageCount).pipe(
map(data =>
const avg = performAverages(data);
return {data, avg}
)
))
);
This way you get an object containing both table data and its average, which is what I understand you are looking for.
I have an API call, that is returning two objects: data and pageOutput.
{
data: "[{"title":["Travis Jackson HOF 1931 W517 # 12 - SGC 50"],"image":["https://i.ebayimg.com/00/s/MTYwMFg5NzY=/z/uU8AAOSwMtdd3ZXo/$_1.JPG"],"itemURL":["https://rover.ebay.com/rover/1/711-53200-19255-0/1?ff3=2&toolid=10044&campid=5338164673&customid=vintagebaseball&lgeo=1&vectorid=229466&item=133253226463"]"
pageOutput: "{"pageNumber":["1"],"entriesPerPage":["100"],"totalPages":["2"],"totalEntries":["194"]}"
}
I have a reducer that is fetching the data from my API and storing data in a a state called 'baseball' like this. Note it is just storing the 'data' piece from my API call.
const baseballReducer = (state = [], action) => {
switch (action.type) {
case "INIT_BLOGS":
return action.data;
export const initializeBaseball = () => {
return async dispatch => {
const baseballCards = await baseballService.getAll();
dispatch({
type: "INIT_BLOGS",
data: JSON.parse(baseballCards.data)
});
};
};
I need to get the pageOutput into a separate state. Now i know that i could create a separate page reducer, make a call to the api and store that pageOutput data in a new state. However, i think that it would be bad practice to make two different calls to the API?
Is there a better way? I was thinking of just storing all of the data (including the pageOutput) in the baseball state, but then I'm not sure if this is best practice or not, especially considering that I have a Pagination control that needs the updated active page to requery the database. Hope this is clear enough--happy to provide any additional code.
There is no need to make two api calls as you already have all data available in a single api call.
Basically you can:
Have two reducers listening to the same dispatched action, each reducer receiving a different portion of the total api response by destructuring the payload to the relevant property (data / pageOutput)
Dispatch two separate actions altogether, one for each reducer.
It doesn't really matter to the app which one you use, so imo it comes down to personal taste.
If you have control over the backend api, I would probably handle pagination a bit differently. Pagination state can be handled fully at the client, and the api would just respond to the client's request (limit / offset - or pageNr, perPage query parameters depending on your taste).
Even though you could therefore return meta data in the api response, you wouldn't need to depend on the api response to manage your state (frontend state change from page1 -> page2 would depend on clicking page2 and configurations defined by the client, not on the response of the api).
I want to save data of every reducer in localStorage in the form of key value pairs
UserReducer: {isLoding: true, ....}
OrderReducer : {isRecieved: false, ....}
So if there is any changes happen in any of the reducer I want to set the updated data in local storage.
What I am thinking is I can do this store.subscribe but how I could know that which reducer has been changed so I can set the whole reducer data to localStorage.
Need help. Thanks :)
One way to do it is to create a lastAction reducer which will store the value of the last dispatched action. Then in store.subscribe you could do:
let nextState = store.getState();
let dispatchedAction = nextState.lastAction.type;
And when you know the last dispatched action, you can update the local storage depending on that action type.
You can create a redux middleware, it will get fired everytime an action is dispatched. Within the middleware you can put a conditional check to see if prev and next store are same. If not, you can update your local storage.
I found this helpful for creating a middleware. And it's fairly easy.
I think you could not know the changes from the store.subscribe.
The last place when you know the changes are the actions.
You can put the persisting logic to the reducers. But maybe you should not want to introduce side effect in your reducer.
So basically you have two options if you rule out the first one.
Persist the whole store as a single object to local storage. It is the easier option. I would try this solution first and I would measure the overhead with a worst case scenario.
Make a redux middleware and capture the individual actions. You can copy the reducer pattern to persist only the object that changed.
You can use subscribe for option 2. You can use middleware for option 2 and option 3.
I came up with one more solution.
You can do the persisting in your action creator with Redux Thunk or with Redux Saga.
You can use React Component for that. Connect it to the Store, and use it to write to localStorage instead of rendering something.
Imho, it would be easier to write than middleware.
You can use redux-watch to listen to the changes in redux store
for e.g.
// ... other imports/requires
import watch from 'redux-watch'
// assuming you have an admin reducer / state slice
console.log(store.getState().admin.name) // 'JP'
// store is THE redux store
let w = watch(store.getState, 'admin.name')
store.subscribe(w((newVal, oldVal, objectPath) => {
console.log('%s changed from %s to %s', objectPath, oldVal, newVal)
// admin.name changed from JP to JOE
}))
// somewhere else, admin reducer handles ADMIN_UPDATE
store.dispatch({ type: 'ADMIN_UPDATE', payload: { name: 'JOE' }})
this may help you.
I want to extend the approach 3 that #PeterAmbruzs shared. Actually the problem which I faced was whenever there was an action dispatched, store wasn't updated immediately but I still wanted to persist it so I imported reducer and passed state and action into it to get the required next state
import reducer from './reducer'
const someMiddleware = store => next => action => {
const state = store.getState(),
const nextState = reducer(state, action)
// Save it to Local Storage
// Or Make an API Call to save it to server
next(action)
}
I'm learning to build a pokemone app that hits the pokemon api, which gives you a lot more info then you need. I'm having trouble figuring out where I should put my code that sifts out the only info I need for my app.
For example, fetching a pokemon's type will return you a type object that looks like this:
{pokemon: [...], weakAgainst: [...]}
If this is what is the data that I'm getting back from my ajax request, and I only want the pokemon array, should
I sift this out in the success of the ajax call that is in my actionsCreators.js and then return that to be dispatched to the store? or
dispatch all of the data type object to my reducer and then in my reducer.js file sift out the pokemon array and copy it to my state? or
copy the whole data object to my state and then sift out the pokemone array in my component's props (that was passed down from mapStateToProps)?
I'm thinking it should be in the reducer? Because it's job is to reduce data to what you need and move it on to the state?
I am of the mind that you should hand the reducer what it needs, if you have the means. Inevitably, you will need to massage and manipulate data in the reducer, but if you can minimize it, I would. Your reducer should be a pure function -- no side effects, the reducers always returns same output given same input etc.. I like to reduce "side effects" by giving the reducer exactly what it needs, especially since the data you need to handoff is so nicely handed to you.. ala.
your actionCreator
export function getPokemanApiData() {
return dispatch => {
axios.get(APIS.getPokemanURL)
.then((response) => {
dispatch(getSuccess(response))
})
.catch((err) => {
dispatch(getError(err))
})
}
}
function getSuccess(response) {
return {
type: TYPES.MY_POKEMAN_TYPE,
payload: response.pokemon
}
}
reducer
case TYPES.MY_POKEMAN_TYPE:
return {
...state,
pokemonData: action.paylod
}
So, the way I see it - try to reduce side effects, minimize the amount of work the reducer has to do. Makes things easier to test too. In the example above, the reducer has exactly what it needs, returns a new state without much fuss. I've read others code in which they like to do the lifting in the reducer, not me. I'd like to hear what others say.
Redux documentations says I should make actions and action creators, like this:
function addTodo(filter) {
return {
type: SET_VISIBILITY_FILTER,
filter
}
}
Then write reducers, like this:
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
});
}
}
I then invoke the action using dispatch:
store.dispatch(addTodo("Ask question on stackoverflow"));
It seems there's a one-one correspondence between actions and reducers; the sole purpose of the action is to select a reducer and provide input data for that reducer.
Why don't we skip the middle man and identify actions with reducers and action creators with functions producing reducers? Then dispatch would take a single argument, a reducer/action of type State => State:
// Action/reducer. (Parametrised state transformer, really.)
const addTodo = text => state => {
return Object.assign({}, state, {
visibilityFilter: action.filter
});
}
// Dispatch takes as a argument the action/reducer
store.dispatch(addTodo("Ask question on stackoverflow"));
You'd lose the ability to serialise actions, but otherwise, it seems you'd get rid of boilerplate action creators and express more clearly the connection between actions and reducers. If you're in Typescript, you also get typechecking of the data in actions, which is difficult to express otherwise.
So what reasons for having actions as data am I missing?
The main purpose of action in Redux is to reduce the state.
Reduce method will be called on array of actions (thats why it called a reducer). Example:
import reducer from './reducer';
const actions = [
{type: 'INIT'},
{type: 'SOME_ACTION', params: {...}},
{type: 'RECEIVE_DATA', data: [...]},
{type: 'SOME_ANOTHER_ACTION', params: {...}},
{type: 'RECEIVE_DATA', data: [...]},
...
];
const finalState = actions.reduce(reducer, undefined);
Action creators is a function that can create actions. It is not necessary that action creator will create only one action.
Actually, if your reducer is able to receive functions instead of objects - your actions will be functions and it will do the main purpose, but you can loose some benefits of Redux functional abilities.
In that case the reducer will be implemented like this:
function reducer(state, action) {
return action(state);
}
The reasons why you may create actions as {type: 'ACTION_NAME'} format:
Redux DevTools expects this format.
You need to store sequence of actions.
Reducer makes state transformations on worker.
Everybody in Redux ecosystem use this format. It's a kind of convention.
Hot reloading abilities (your stored functions will not be reloaded).
You need to send actions as is on server.
Debugging benefits - to see the stack of actions with actions names.
Writing unit tests for reducer: assert.equal(finalState, expectedState).
More declarative code - action name and parameters are about "what to do" and not about "how to do" (but addTodo('Ask question') is declarative too).
Note about coupling between action creators and state changes
Just compare two notations:
First:
function someActionCreator() {
return {
type: "ADD_TODO",
text: "Ask question on stackoverflow"
}; // returns object
}
Second:
function someActionCreator() {
return addTodo("Ask question on stackoverflow"); // returns function
}
"In both cases we see that code is declarative and action creator is decoupled from state change. You can still reuse addTodo or dispatch two addTodo's or use middleware or dispatch compose(addTodo('One'), addTodo('Two')). The main difference is that we created Object and Function and place in code where state changes.
There is NOT a one-to-one mapping between actions and reducers. Per Dan Abramov's comments at https://github.com/Pitzcarraldo/reduxible/issues/8 :
It reinforces a very common misconception about Redux: namely that action creators and reducers are one-to-one mapping.
This is only true in trivial examples, but it is extremely limiting in real applications. Beginners exposed to this pattern couple reducers and action creators, and fail to realize they're meant to be many-to-many and decoupled.
Many reducers may handle one action. One reducer may handle many actions. Putting them together negates many benefits of how Flux and Redux application scale. This leads to code bloat and unnecessary coupling. You lose the flexibility of reacting to the same action from different places, and your action creators start to act like “setters”, coupled to a specific state shape, thus coupling the components to it as well.
As for actions and the "type" parameter, the other answers are right. That's deliberately how Redux was designed, and that was intended to give the benefits of serialization for debugging purposes.
Good question.
Separating actions from state changes is really a Flux pattern, rather than a specifically Redux thing. (Though I will answer the question with reference to Redux.) It's an example of loose coupling.
In a simple app, tight coupling between actions and state changes might be fine. But in a larger app, this could be a headache. For instance, your addTodo action might trigger changes in several parts of the state. Splitting actions from state changes - the latter performed in reducers - allows you to write smaller functions, which are easier to reason about and more testable.
Additionally, decoupling your actions and state changes allows your reducer logic to be more reusable. e.g. Action X might trigger state changes A and B, whilst action Y only triggers state change A.
Furthermore, this decoupling gives rise to a Redux feature called middleware. Middleware listens to action dispatches. It doesn't change the state of the app, but it can read the current state and the next state, as well as the action information. Middleware is useful for functionality extraneous from the core application logic, e.g. logging and tracking (dev tools were mentioned in a previous answer).
[UPDATE] But why have actions as objects?
If it were simply a matter of functions calling other functions, that decoupling would be much less explicit. Indeed it may even get lost; since most actions only enact one state change, developers might tire of a single function calling a single function and do away with the separation entirely.
The other thing is the Flux data flow model. One-way data flow is very important to the Flux/React paradigm. A typical Redux/React goes something like this: Store state -> Higher order React components -> Lower order React components -> DOM. The action is the aberration in the model; it's the backwards arrow transmitting data from the view to the store. It makes sense to make the action something as loud and emphatic as possible. Not a mere function call, but a dispatched object. It's as if your app were announcing Hey! Something important happened here!