ngrx create nested state by domain and ui data - ngrx

I'm using ngrx entities, and ideally I would like to achieve the following state structure:
state = {
domainData: {
userEntities: {
entities: {},
ids: []
}
},
ui: {
usersView: {}
}
}
This will allow me to separate my data storage from UI related stuff. I can't seem to find a way to do this though. At the moment what I'm doing is this:
I have a separate store module. I have created a reducer for userEntities and a reducer for usersView. Then, with ActionReducerMap I have created a combined reducer like so:
const reducer: ActionReducerMap<AppState> = {
userEntities: userEntitiesReducer,
usersView: usersViewReducer
}
Then I import StoreModule.forRoot(reducer) in my store module. It works, but the structure of the resulting state is not how I would like it to be. Is there any way I can fix it?
Thanks.

Related

Using a Map inside of Vuex state

I'm building a prototype module to learn about Vuex and running into what seems like a simple issue -- I'm having trouble using a Map as one of my state variables.
I want to use Vuex to store some basic user preferences across multiple modules and figured a Map would be a simple way to do it since the prefs could be handled as simple key/value pairs. But I'm either not defining the Map correctly, or I'm not using it correctly within the mutations.
<script lang="ts">
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
userSettings: Map,
},
mutations: {
addUserSetting(state, payload) {
if (state.userSettings == null) {
state.userSettings = new Map();
}
state.userSettings.set(payload.key, payload.value);
}
},
})
</script>
I'd be willing to use another object, and I started with an array of UserPref objects but that didn't go well, either.
Vue 2 doesn't have proper support for Map.
Assuming your keys are strings I suggest using an Object instead. To get a totally empty object (with no inherited properties, not even toString) you can use Object.create(null), or just use a normal object using {} if that seems unnecessary.
e.g.
import Vue from 'vue'
export default new Vuex.Store({
state: {
userSettings: Object.create(null),
},
mutations: {
addUserSetting(state, payload) {
Vue.set(state.userSettings, payload.key, payload.value);
}
}
})
Vue.set must be used as the properties are being added.

Redux Selectors in Mithril

I've been tasked with implementing selectors in our redux application. Everything I'm reading online about redux selectors talks about React and how you can replace what is in mapStateToProps with a selector. What is the equivalent/where would i do this in a mithril app?
What is the equivalent/where would i do this in a mithril app?
Firstly, you don't need an equivalent, you can just use the exact same selectors that you would in a React application.
Where to call selectors?
You can call the selectors wherever you want, but I recommend calling them as close to where the data is used as possible. Don't call selectors in a component high up in the component hierarchy only to pass the data down via several components before they end up in a component that actually uses the data – unless you have a good reason to do so.
For most cases you can call the selectors inside a view-function, although you might come across cases where you need to call selectors in other lifecycle methods as well. In some applications you might also want to use selectors in m.render as well.
A couple of examples off the top of my head:
Inside the view function when creating DOM-elements
var LoggedinUserDetails = {
view: function () {
return m('', [
m('', getLoggedinUserName(store.getState())), // getLoggedinUserName is a selector
m('img', { src: getLoggedinUserImageUrl(store.getState()) }) // getLoggedinUserImageUrl is a selector
])
}
}
Inside the view function when creating Mithril components
var UserDetails = {
view: function (attrs) {
return m('', [
m('', attrs.user.name),
m('img', { src: attrs.user.imageUrl })
])
}
}
...
m(UserDetails, { user: getLoggedInUserDetails(store.getState()) }) // getLoggedInUserDetails is a selector
Inside m.render
In this example, we have a game that requires us to re-render the whole page after any change.
function onStateChange() {
var state = store.getState();
m.render(document.body, m(Game, {
map: getMap(state),
players: getPlayers(state),
visibleArea: getVisibleArea(state)
}));
}
// Receiving partial state updates from server via websockets
websocket.on('update-map', function (map) {
store.dispatch({ type: 'update-map', payload: map });
});
websocket.on('update-players', function (players) {
store.dispatch({ type: 'update-players', payload: players });
});
// Changing state based on user input
window.addEventListener('keydown', function (event) {
switch (event.key) {
case 'Enter':
store.dispatch({ type: 'move-visible-area-to-next-player' });
break;
}
});
I'm not familiar with Mithril, but Redux state selectors are independent from React. They are just functions that expect state and return a:
a slice of state
or data derived from state
For example, if I my Redux state has an entry named records containg a list of models:
{
records: [ ... ],
}
I could create a selector returning the length:
const numOfRecords = state => state.records.length
Or if my state also keeps track of a sortBy value:
const sortedRecords = state => state.records.sort(sortByFn(state.sortBy))
Selectors can be helpful to increase performance and reduce the need for updates. (reselect is a great module for this).
They are also great for developing modular pieces of code that depend on data stored in application state but don't really want to know where that data comes from.

How to get createContainer & Collection working outside of main jsx

I'm a novice to Meteor/React.js applications, and while I've been able to cobble together an app with multiple components by passing the App.jsx collections around as props, I can't seem to get individual components to handle their own collections. My rational is that some components need the collection fetched in one order, while others need the collection fetched in another. It seems silly to pass around a collection and then have to manipulate it internally to get the proper effect when a new sort is what's needed. My main App.jsx has:
App.propTypes = {
workdone:PropTypes.array.isRequired,
subjects:PropTypes.array.isRequired
};
export default createContainer(() => {
return {
workdone: WorkDoneCollection.find({},{sort:{createdAt:-1}}).fetch(),
subjects: SubjectCollection.find({},{sort:{subject:1}}).fetch()
};
}, App);
and if I pass the props.workdone on to another component everything works fine. On the other hand if I try a set up an independent collection in the other component, like this:
WorkSummary.propTypes = {
workdone:PropTypes.array
};
export default createContainer(() => {
return {
workdone: WorkDoneCollection.find({},{sort:{createdAt:1}}).fetch()
};
}, WorkSummary);
the workdone property is undefined.

Redux best practice for sharing data between states

I'm working on an webapp using redux, and I'm trying to figure out the best way to sharing data between two substates.
Lets say I have a data structure like so, for an app where a user can order food from an italian restaurant:
{
"entrees": [{ "type": "pizza", "sauce": "alfredo", "topping": "meat" }],
"options": {
"entreeTypes": ["pizza", "calzone"],
"sauceTypes": {
"pizza": ["marinara", "white"],
"calzone": ["marinara", "alfredo"]
},
"toppingTypes": {
"marinara": ["meat", "veggies", "cheese"],
"white": ["meat", "veggies"],
"alfredo": ["meat"]
}
}
}
So if the user wants to add an entree to their order, the new entree should populate all the default options for type, sauce, and topping, which here would be "pizza", "marinara", and "meat".
Ideally, I'd like this addition of an entree to take place in the entrees reducer. However, the entrees reducer would need access to various parts of the options state to accomplish its goal.
My present implementation looks something like this (using redux-thunk):
function addEntree(){
return (dispatch, getState) => {
const options = getState().options;
const type = options.entreeTypes[0];
const sauce = options.sauceTypes[type][0];
const topping = options.toppingTypes[sauce][0];
const action = { type: 'ADD_ENTREE', type, sauce, topping };
dispatch(action);
};
}
Where the action gets picked up by the entrees reducer.
So my question is, does this seem in good practice? Is anyone else using patterns they find to be superior?
The example is a but odd because typically options aren't part of the application state. Moreover, it seems like you want a hierarchy of choices, where you can't have veggies with your alfredo. So two things:
If the options can be constants outside of application state because they are dynamic, do that. So basically store your current options reducer as a const somewhere and you can just import it.
If the ingredient list and combinations are dynamic, I don't think you should be enforcing the ingredient combination restrictions in your actions. If you have a order page, for example, load all of the options in the props. Voilá, you have access to multiple reducers at once. In the situation where the user has selected "Pizza" but not the sauce or topping, you would select the first sauce in props.options.sauceTypes[selectedEntree], and then props.options.toppingTypes[selectedSauce]
class OrderPage extends React.Component {
...
function onSelectEntree (entree) {
const { options: { sauceTypes, toppingTypes }, addEntree } = this.props;
const sauce = sauceTypes[entree][0];
const topping = toppingTypes[sauce][0];
addEntree(entree, sauce, topping):
// Or if you don't want to persist it globally yet:
// this.setState({ entree, sauce, topping });
}
...
}
Then in the view, show only the options that are allowed given the current selection that the user has made (ie, hide veggies if they picked alfredo). Do validation in the component.

Is passing the entire state down the react component a good pattern?

The Flux documentation says that:
We often pass the entire state of the store down the chain of views in a single object, allowing different descendants to use what they need. In addition to keeping the controller-like behavior at the top of the hierarchy, and thus keeping our descendant views as functionally pure as possible, passing down the entire state of the store in a single object also has the effect of reducing the number of props we need to manage.
In this pseudo code (illustrating the passing of the whole store as suggested by the Flux docs), the interfaces of the components don't seem to be very well defined.
function header (state) {
profileLink(state);
navigation(state);
}
function postList (state) {
ul(state);
}
function footer (state) {
div(state.copyright);
}
function rootComponent () {
var state = getState();
header(state);
postsList(state);
footer(state)
}
Isn't the following example (with more specific interface of the components) more in accordance with the functional programming paradigm?
function header (userName, navigationItems) {
profileLink(username);
navigation(navigationItems);
}
function postList (posts) {
ul(posts);
}
function footer (copyright) {
div(copyright);
}
function rootComponent () {
var state = getState();
header(state.user.name, state.navigation.items);
postsList(state.posts);
footer(state.copyright);
}
The question is: Isn't the passing of the whole state down the component hierarchy an anti pattern?
I think what he means is sometimes is convenient to pass the entire state of one specific store down the children. It makes sense if all the children are related to the store. For example:
function rootComponent(userState, bookState){
bookTitle(bookState);
bookDescription(bookState);
bookPrice(bookState);
}
bookTitle(bookState) {
div(bookState.title)
div(bookState.author)
}
bookDescription(bookState) {
div(bookState.description)
}
bookPrice(bookState) {
div(bookState.price)
}
So, as you can see, I'm not passing the whole state of the app but all the state coming from the book store is being sent packed to all the children related with this store

Resources