Given the following redux state tree:
{
app: {...},
config: {...},
page_data: {...}
}
How would I replace the contents of page_data with an entire separate reducer depending on the page a user is on?
For example I could have three reducers user, products, competitions. If I switched from a user page to a product page I'd want the page_data branch to show:
{
page_data: {
productPage: {...}
}
}
with no reference to user as I don't want to bloat the app state and also don't need that data on the product page.
Note: I'm using combineReducers for reference.
Is this possible and what is the best approach?
Using same name for multiple reducers is definitely wrong. Not just it isn't supported, it's wrong practice. You can achieve this using FLUSH_PAGE_DATA action. Dispatching this action will flush the page data in all the reducers. It'll Look something like this.
case 'FLUSH_PAGE_DATA':
return { };
Then based on you active page, which you'll pass in every action, you can create different structure of page data.
Related
I'm using Normalizing State Shape for my Redux Store and it looks like this:
entities: {
users: {
list: [], // <-- list of all my users
loading: false,
lastFetch: null,
},
}
I got stuck on what should I do if someone opens up a website directly on the user's detail page. For example: {WEBSITE_URL}/users/1. The Redux Store is empty and I need to request only one entity. Should I:
fetch the whole list, put it in the Store and select one requested entity?
fetch only user #1, put it in the Store user list (entities.users.list), set lastFetch to null (this is because if someone will redirect to list next, he will fetch the new list again. Clearly the pervious list didn't have all users), and display user #1 from the list.
fetch only user #1, put it in the Store in separate place. For example in selected field of users:
entities: {
users: {
list: [],
loading: false,
lastFetch: null,
selected: null // <--- HERE
},
}
What solution do you think is the best? Do I need selected field at all? All tutorials and courses don't mention this scenario, only scenario how to fetch the list.
I'm having the same dilemma.
My approach is always — 3. I'm creating selected/single state to load data and one additional action (e.g. clearSelectedUser, clearSelectedPost) to clear data from the store on component unmount.
I'm also using Redux Saga to fetch data (do async operations) and this works good as combo. I really like the idea of having neat components without async calls in it.
However, I also found it acceptable to use component state (with useState hook) and do data fetching from a component directly (without Redux Saga or the store) in this particular case (entity single page/screen).
Option 1. will not work if you get paginated data from your API. You'll just complicate things.
Option 2. I agree with you on that one.
I'm making some updates to one of my VueJS apps, and there are a few different ways I could go about it. I wanted to know what the best practice was in terms of performance, or if there isn't much of a difference at all.
Essentially I have a couple of different pages, each of these pages present results in a table. I'm using Boostrap to manage the tables. What I want to do is have a button at the far end of the table that opens a modal where you can see detailed results for that specific line entry in the table.
Now because there are tables on different pages and I want the same behaviour, I want to have the modal / button in their own component, that way I can just copy-paste the component from one page to the next and have the exact same behaviour.
My question is this, is there a difference between :
Having the modal on the page itself, and having all the logic in the page itself
Having a component containing the modal and the logic, and repeating that component 1000s of times in the different tables
Is there any performance difference between having the modal repeated 1000 times in the table versus having only one modal in the page? My understanding is that the modal wouldn't be loaded until you click the button anyway, so would it have an impact on performance?
The way that I would approach this would be to use a Vuex store. When a user clicks the button just go set the data in the store, and then have your modal component import this same store and get the data it needs. That way whenever you want to use the modal anywhere in the application you can as long as the data it needs has been set in the store, and you'll (hopefully) have a nice design to just do something like this.$dialogs.popMyCoolModal(data)
Something like this:
...mapActions({
setButtonData: actionTypes.SET_BUTTON_DATA
})
Then on click of the button just call this mutation, pass it the payload that it needs. I'm just calling it the horrible name of buttonData since I'm not sure what your data actually looks like.
In your store:
export const actionTypes= Object.freeze({
SET_BUTTON_DATA: `setButtonData`
});
export const actions: ActionTree<State, State> = {
async setButtonData({commit, state}, buttonData: ButtonData) {
const data = await someApi.getData(buttonData);
commit('setButtonDataState', data);
// Pop your modal here... how you pop it entirely depends upon your modal implementation
}
};
export const mutations: MutationTree<State> = {
setButtonDataState(state: State, data: SomeData) {
state.data = data;
}
}
Then in your store after you set the data you can simply pop the modal component in the action, the modal component will take care of the rest.
We are trying to split our project on to pieces:
We have libraries e.g user-library and image-library.
Each library has their own reducer, and actions e.g:
for user-library:
{isloggein: false} // store
function logIn() { //action creator
return {
type: 'LOGIN'
};
}
for image-library:
{images: []} // store
function addImages() { //action creator
return {
type: 'ADDIMAGES'
};
}
Now we decide that we want addImages as soon as user logIn. Problem is image store now nothing about user actions (that is main idea of code splitting). We solved that problem by using sagas like this:
function* addImagesWhenLogIn() {
while (true) {
yield take('LOGIN');
yield put(addImages());
}
}
But it is not opaque (we dispatched one action but 2 actions will be dispatch actually). While it is fine to have business logic of application in sagas, I believe that take -> put sequence is not the best solution in such case, and I am looking for better way of doing it.
Some of the advantages of redux architecture is the capability to easy hydration, snapshots, time travel, and it works best if there is a single place where state is stored.
From redux docs:
Single source of truth - The state of your whole application is stored
in an object tree within a single store. Ref.
Therefore a unique state for your redux app is recommended.
In your case I would suggest:
You could keep separated your libraries/modules, import them as you need in your app.
Use only a single state tree as suggested in redux docs.
Provide state information to your representational components using containers.
I think that's a perfectly valid approach. Your two main options are to either have one module import action creators from the other module (thus somewhat coupling the two directly), or have a separate piece of code that glues the two together. Using a saga like that is a very good approach for handling the "glue" aspect.
I have a simple chat application going on and the following stores:
MessageStore - messages of all users/chat groups
ChatGroupStore - all chat groups
UserStore - all users in general
I'm using immutable.js to store data. The thing is, MessageStore needs to use data from ChatGroupStore and UserStore, each message is constructed like this:
{
id: 10,
body: 'message body',
peer: {...} // UserStore or ChatGroupStore item - destination
author: {...} // UserStore or ChatGroupStore item - creator of the message
}
How am I suppose to update MessageStore items according to ChatGroupStore and UserStore update?
I was using AppDispatcher.waitFor() like this:
MessageStore.dispatchToken = AppDispatcher.register(function(action) {
switch(action.actionType) {
case UserConstants.USER_UPDATE:
AppDispatcher.waitFor([
UserStore.dispatchToken
]);
// update message logic
break;
}
});
From my point of view I would have to wait for the UserStore to update and then find all the messages with the updated user and update them. But how do I find the updated peer? I think a search in UserStore by reference wouldn't be enough since immutable data doesn't keep the reference when data changes, then I would have to apply more on queries. But then I would have to apply query logic of other stores inside MessageStore handler.
Currently I'm storing peers as a reference inside each message, maybe should I change to just:
{
id: 10,
peer: {
peerType: 'user', // chatGroup
peerId: 20
}
}
Would be great if anybody could shed some light about it. I'm really confused.
The best option I can see as a solution in all occasions is not to keep related data nested and to avoid transformations on data that comes from server, this will reduce the amount of work I need to do to keep the data up to date at all times. Then in your view, all you have to do is to subscribe to changes and put together the necessary data.
Alternative to Flux
There's also a good and well maintained state container solution called Redux which I suggest everyone to at least try. It has only one store and combines the whole state into a single deep object, although you can create each reducer separately. It also has a good way to integrate it with React, see Usage with React.
I am new to react-redux world and having some trouble visualising a piece of complex data flow (I think).
Assume the state contains a collection of tracks and an array of favorite track ids. User could favorite a track from a number of various components e.g. musicplayer, tracklist, charts and all the others would have to rerender.
At the moment, I'm triggering an action to add/remove the track id to/from the favorites array. But I can't quite see how to proceed from there.
My plan is to trigger another action for e.g. the trackItem reducer to listen and carry on. Or could each related component directly subscribe to changes of the favorites collection? Or can I have two reducers listening to the same action? I have now idea how to implement something like that and also I have a gut feeling that I'm on the wrong path.
Feels like I'm struggling to get rid of my backbone-marionette habits. How would you do it?
My other plan is to have an isFavorited boolean within the track item json and use an action/reduces to update/toggle that property. I understand that normalizr will merge instances with the same id, so any component subscribed to its changes will react.
Or could each related component directly subscribe to changes of the
favorites collection
They could. But do these components all share some parent component? If so I would have that parent component subscribe to the state change of the favorites array, and pass that down as props to the components that need it.
I would recommend really reading through the redux docs: https://rackt.github.io/redux/
Especially usage with React: https://rackt.github.io/redux/docs/basics/UsageWithReact.html
Typically you would have a 'smart' component that renders for a route, and that would subscribe to the redux store and pass down the data its nested 'dumb' components need.
So have your smart component(s) subscribe to the state change of the favorites array and pass it down as a prop to the components that need it.
It's all right to listen to one action in more than one reducer, so maybe go down that route?
Do your components share common parent component? If they do, connect it to your redux app state and pass favorite ids array down to each one; then dispatch action addFav or removeFav from any component, react in favorites reducer and see redux passing new props to react components.
I think you should first understand about smart and dumb components in reactjs, here is the link, so you will be having a single smart which connects to you reducer and updates your child(dumb) component.
If you still wants to have two reducers, you can have a action which executes its operation as a result it calls another action. to achieve this you need to have a redux-async-transitions, the example code is given below
function action1() {
return {
type: types.SOMEFUNCTION,
payload: {
data: somedata
},
meta: {
transition: () => ({
func: () => {
return action2;
},
path : '/somePath',
query: {
someKey: 'someQuery'
},
state: {
stateObject: stateData
}
})
}
}
}