I have some asynchronous middleware that accesses a remote api and dispatches it to a Redux.
I am accessing an existing api which returns a large chunk of data, much of it that I do not need. Is there any established best practice for when to discard unwanted elements from the data? As far as I can see I could:
1 - Filter it out when received and only pass what I need to the store.
2 - Store everything in the store when it is received and use a selector or mapStateToProps to extract only what I need to render.
3 - Store and extract everything and filter out what I need within the component.
What do others think?
In case you can't change the API to use something like streams or at least pagination, go with option no. 1.
Try to work with the least amount of data necessary to do the
job. This is a general rule and doesn't apply only to
redux!
Try to keep your store as flat as possible.
Try to minimize the data involved in any actions that lead to a change in the store
With that said, filter out all unused data right when the API response comes in.
If you're getting a large amount of data something like this from the API:
API
"payload": {
"info": [ ... large ...],
"meta": [ ... small ...],
}
The in your Redux action action make sure to use only the small data. For instance in your reducer
Reducer
function reducer(store, action) {
switch (action.payload) {
case 'GET_API':
store = {...store, meta: action.payload.meta }
}
break;
default:
break;
}
return store
}
So now, you wont have that large data anymore, once that API is finished, the reducer will only contain the the small data.
Related
I am trying to implement undo/redo in a complex application
This is how my history array looks.
[
{
action:“added”,
payload:{
id:"32132132",
data: "JSON.Stringify(JSON.parse(data))) initial data getting from initial app setup"
}
},
{
action:“updated”,
payload:{
id:"32132132",
data: "getting the diff"
}
},
{
action:“deleted”,
payload:{
id:"32132132",
data: "data"
}
}
]
As far as I understood, the undo, redo works on a history index,
which will be changed based on undo (increment pointer) , redo (decrement pointer) concept.
Based on this approach I am calling a mutation where
apply(state,actiontype){
if(actiontype==undo){
remove the respective objects
pop(data) //pops the data out of other places in the application not the history
historyindex++;
}else if(actiontype==redo){
get the data from history
unshift(data);
historyindex–-;
}
}
I feel this is not the most efficient way to perform undo/redo operations, as it includes cloning objects and even it has to handle huge sets of data. Which might lead to freezing of the application I am still a newbie in vuejs, please do correct me if I am wrong, is there any more efficient way to perform undo-redo operations ? or the right way to implement undo/redo in vuejs?.
Any suggestions would be helpful
You should consider use some kind of data compress (like git does), with this you can continue using only the Vuex. Otherwise, consider use some local database as already recommended ;D
you may want to connect your app to any simple database that store your previous object try firebase if it has no backend and make vuex just have the previous value but older ones saved to the firebase database by the mutation method in vuex
We're planning to use Redux with Redux-thunk in our React App. Typically, every time we open a page in our browser (from a menu or whatnot) we usually call some Action Creator from Redux. And let's say those action creators make a rest API call and finally fetch some data from a remote site, and store that info in our Redux store.
From the starting point, i.e. when I first load the app in my browser, every time I change a page the store will get larger. That means the memory print of my application grows as I change from the Employees Page to the Tasks Page, for example, etc.
What's the best practice in a typical Redux App? Is there a way to downsize the store as we move along the pages?
In the above example, I might not need the employee info anymore when I move to the Tasks Page but that info still takes memory space in my Redux Store. I'll probably need to re-fetch that Employee data if I move to the Employees Page again, anyway. So I feel like there is no point keeping that info in my store when I'm cruising within other pages of my app.
This is not a problem for many applications out there, but if we are working with large size data, or if we have too many pages in our app, then the memory problem really hits us.
It depends entirely on how you manage your state in your reducers. If you setup your reducer to replace the data in the store then no, the store will not grow larger.
function employeesReducer(state = initialState, action) {
switch (action.type) {
case SET_EMPLOYEES:
return {...state, employees: action.employees};
}
return state;
}
In another case, you can have a list in your reducer state and action that appends to the list indefinitely then yes, the store will grow larger.
function employeesReducer(state = initialState, action) {
const newState = {...state};
switch (action.type) {
case ADD_EMPLOYEES:
newState.employees.append(action.employees)
}
return newState;
}
You need a clear/reset mechanism in the above case.
This above is just a simple example but in a large application you can get "leaky" reducers where your dataset expands but that would be due to programming error or constantly appending data.
Another example where memory can constantly expand is if you implement a redux undo where you save your state each time so you can replay it. Here is a library that does that.
I have a large app with some autocomplete text-inputs that retrieve search suggestions from the backend on every key stroke.
I want to save recent search query results to avoid multiple backend calls for the same query if the user deletes characters. I also want to expire these queries after some period of time to keep search results fresh.
I also want to have a loading indicator for the current text input if the backend call isn't complete yet for that exact value.
The question is - where to manage this state and the actions associated with this state (below I have a sample state shape I need).
Managing it in the main Redux store looks like an overkill - this state is kind of temporary-intermediate state, it's very short-lived (queries should be expired after a while), there may be many instances of the same Component present on the screen, and different instances might use different backend calls.
Managing the recent search-queries and search-results in the local React Component state object - looks like a fine solution.
But now we have the backend calls which I don't want to fire from within the component, but go through the full-blown Flux process, with proper actions and reducers and passing the results (or errors) into props through the Store Connector.
So eventually, things here don't fit properly with each other - I don't want to manage the state in the main Redux store but I do want the backend-calls (results of which are the main part of that state) to go through the main reducers+store lifecycle.
Any advice for a robust, maintainable, and easy-to-figure-out-without-docs architecture is appreciated.
The state I need for every instance of this component looks something like:
(let's say I typed in dog, and the last result didn't come yet):
{
currentSearchInput: 'dog',
recentQueries: [
{
input: 'd',
isLoading: false,
results: [...]
},
{
input: 'do',
isLoading: false,
results: [...]
},
{
input: 'dog',
isLoading: true,
results: null
},
]
}
In my opinion, using the object structure noted above with the components internal state is just fine. Write a function that will accept the results of the HTTP Request that come back through Redux, and update the results field of the appropriate object in the array. Personally, I would store the promise in the results field. Each time componentWillReceiveProps gets called (due to your reducers returning new redux state) use a setState to update your recentQueries.
I'd also set a limit. 5 recent query max or something like that.
That being said, I use the google places API to return address/establishment suggestions and the data is so small it wasn't worth it to do this. Most of the results are under 10kb.
EDIT:
Based on our discussions in the comments, here is what I would suggest doing.
In the onChange handler for the input.
Pass the search criteria to the action as you normally would
In the action creator, return the results of the API call AND the search criteria
In the reducer
Check to see the length of the array that holds your search calls (the redux store value). If it is less than five, simply concat the current values and the new values and return. If its value, overwrite the 0th position of the array with the new content. e.g.
state[0] = action.newApiResult; return state;
Per documentation on normalizing state shape, data of list in nature are to be stored by Id in the state tree as an object, which means array data returned from API calls have first to be converted to object in action creators. The object data will then have to be converted back to array in mapStateToProps() in order for the resulting props to be rendered using .map() function of array inside render(). Is this back-and-forth conversion a reasonable cost for normalized state shape? Or is there a better design pattern for handling list data in using ReactJS with Redux?
Yeah, what you're describing is what you have to do if you store normalized state and then want your components to consume an array.
Usually, my state will be something like this:
users: {
byId: {
// map of objects keyed by id
},
ids: [//array of ids],
}
Note that I'm keeping ids around to keep track of the order the items were returned from the server; that may or may not be necessary depending on the use case. You may be able to just store the object and use Object.keys if the order is not important (probably because you end up sorting by some other criteria).
Typically, the most consumable form of the data will be an array, so you would define a selector to get the users. Following the pattern of colocating reducers and selectors, you would have
// User reducer
export const getUsers = (state) => state.ids.map(id => state.byId[id])
and
// Root reducer
import users, * as fromUsersReducer from './users
const reducer = combineReducers({
users,
})
export const getUsers = (state) => fromUsersReducer(state.users)
And then your container
import { connect } from 'react-redux'
import { getUsers } from 'reducers/root'
import UsersComp from './users
const mapStateToProps = (state) => ({
users: getUsers(state),
})
export default connect(mapStateToProps)(UsersComp)
In terms of whether this conversion is a reasonable cost or not, it depends on the use case. Where normalized state shape gets to be really important is when you're data can be edited, or your data is related to other pieces of data (getting all posts from a user etc.). If, however, all you're doing is getting a list of data and all you ever do with it is just display that list, then normalizing is probably not necessary.
If you end up needing to add more complex operations for the data down the line, it is easy to switch assuming you are being strict about only accessing state via selectors. Normalized data is generally seen as a best practice simply because you generally are going to end up doing more complex things with your data where having a normalized shape is advantageous. But it's not a hard rule or anything; there are cases where it's not needed for sure.
The only real costs you are looking at here is
Code to perform the normalization/denormalization.
Performance hit if your lists are very long, having to denormalize on every render of a container.
You can mitigate the performance hit pretty easily using reselect. This is oftentimes not necessary. In terms of the extra code, it's basically the same sort of stuff over and over again, so it can be pretty easy to abstract, and the code itself is really not complicated anyways. It's a lot less complicated than reducer/selector code would be in a complex app if everything were stored as arrays.
I having some issues on how to conceptualize a react application with Redux being my Flux library of choice.
So I am taking a few assumptions from my readings, correct me if I am wrong,
How does one manage data fetching?
Let's say this, I have a application that needs to fetch some data specific for the current logged in user, I assume this user data should be stored in the Redux Store.
But now the problem ensues, if all my state data is stored in a store, do I have for example a array of messages in the store for this user, and make my component fetch the information from the store? Or should I Fetch the data on the componentWillMount or similar method? I get that when I need to fetch data the first time, I will send an action to the store to fetch the data from the server, which will trigger a change event that I can catch on the component and update the state, is that a correct?
I feel like I am missing a point somewhere and can't make the connection on how the app is supposed to be structured and manage the data, since it seems the store will be bloated with tons of smaller "state" objects that will be used across the other routes/components.
In Redux you have a single store with many reducers. Each reducer changes specific part of the state.
For example you have an state like this:
{
messages: [...],
currentUser: {...},
notifications: [...],
...
}
In this case you will have a reducer to change messages part of state. Another reducer to change notifications part.