function in Redux state - redux

during my react-redux application development, I have run into a special use case:
Our application take some functions as resources (we dynamically load them, then use them as factory to create instances). Currently I put them in the store state and it works well. However, it seems kind of anti-pattern to put functions in Redux state, which will break the ability to persist and rehydrate the contents of a store.
So I try to move the functions out from the store state: I use a map to save functions and store the corresponding key in the state. The question is, by doing so, my state-to-ui transform no longer retains a pure function. As the functions are dynamicly loaded, content of the functions-map mutates. In different period (e.g. functions loading VS functions loaded), the same store state will result in different UI.
Now I feel that it's not so correct to put the functions in some place out of redux system. I need some advices about where to put these functions.

Storing functions in Redux state could be not a good idea exactly because of reasons you mentioned.
You can store in Redux state loading status of your functions. Something like:
{
'loadedFunctions': ['func1', 'func2']
}
or something more complex:
{
'functionsStatus': {
'func1': {
loaded: false,
loading: true
}
}
}
In that case same Redux state should result same UI (if body of your functions doesn't change)

Related

redux-injectors: Using yield select in saga before reducer and saga are injected

Hello this is my first question. I am trying to set up a project where modules along with the redux and sagas will be injected into the main app, using redux-injectors. In my sagas I want to use yield select, to check if an action has updated the state and then carry on. For example, when I post an image, I want to make sure there were no errors in posting the file and then move on. I use the following function:
export const imageErrors = (state: RootState): IImagesErrorState => state.image.errors
and then in the saga.ts file I use it as such:
if (imagesErrors?.postImageError !== null) {
throw imagesErrors.postImageError
}
this works fine as long as the state.image exists in the root state from the beginning. However, how do I do that when I want to inject this state later on using useInjectReducer and useInjectSaga? I obviously get an error
Property 'image' does not exist on type 'Reducer<CombinedState<{ user: CombinedState<{ auth: IAuthState; errors: IErrorState; }>; }>, AnyAction>'.ts(2339)
So how do we handle selectors of specific pieces of state, since state does not yet include them?
Thank you so much.
Can't talk about the Typescript part of things, but in terms of architecture you've got two options.
One is the obvious - that is to add conditions or ? everywhere to avoid errors from accessing missing properties, but that can get tedious quickly.
The other probably better option is to rethink your state & application chunks. What is this saga that is accessing state that isn't existing yet? Does it need to run before you have such state? If not, let's move the saga to the same chunk as the reducer. In the opposite case, where you need the saga to be running e.g. as part of the runtime chunk, then perhaps the image state should be in the runtime chunk as well.

Redux / Flux Pattern for Fetching Data When Store Updates

I have what I believe is a very common scenario... I'm building a dashboard of components that will be driven by some datasource. At the top of the view would be a series of filters (e.g. a date range). When the date range is updated, the components on the screen would need to update their data based on the selected range. This would in turn force the individual components that are slave to that picker to need to fetch new data (async action/XHR) based on the newly selected range.
There can be many components on the screen and the user may wish to add/remove available displays, so it is not as simple as always refreshing the data for all components because they may or may not be present.
One way I thought to handle this was in the action dispatched when a new date range is selected was to figure out what components are on screen (derived from the Store) and dispatch async actions to fetch the data for those components. This seems like a lot of work will go into the DATE_CHANGED action.
Another alternative might be to detect date range changes in store.subscribe() callbacks from each of the components. This seems to decouple the logic to fetch the data from the action that caused this to happen. However, I thought it was bad practice (or even an error) to dispatch while dispatching. Sure I can wrap it in a setTimeout, but that feels wrong too.
Third thing that came to mind was just doing fetch calls directly in the component's store.subscribe() and dispatching when those return, but I thought this breaks the connect model.
This seems like a common pattern to fetch based on state changes, but I don't know where its best to put those. Any good documentation / examples on the above problem?
Don't use store.subscribe for this. When DATE_CHANGED reaches the reducer it's meant for, simply change the application state (I'm assuming the date range is part of the store somehow). So you have something like state.rangeStart and state.rangeEnd.
You didn't mention what view rendering library you're using, so I can only describe how this is typically done with React:
The components know wether they are currently mounted (visible) or not, so redux doesn't need to be concerned with that. What you need is a way to detect that state.rangeStart or state.rangeEnd changed.
In React there is a lifecycle hook for that (componentWillReceiveProps or getDerivedStateFromProps in the newest release). In this handler you dispatch async redux actions that fetch the data the component needs. Your view library will probably have something similar.
The components display some kind of "empty" or "loading" state while you're waiting for the new data typically. So a good practice is to invalidate/clear data from the store in the reducer that handles the DATE_CHANGED action. For example, if state.listOfThings (an array) entirely depends on the date range, you would set it to an empty array as soon as the date changes: return { ...state, listOfThings: [] }. This causes the components to display that data is being fetched again.
When all the async redux actions went through the REQUEST -> SUCCESS/FAILURE cycle and have populated the store with the data, connected components will automatically render it. This is kind of its own chapter, look into redux async actions if you need more information.
The tricky part are interdependencies between the components and the application they're rendering. If two different dashboard components for example want to fetch and render state.listOfThings for the current date range, you don't want to fetch this data twice. So there needs to be a way to detected that 1) the data range has changed but also 2) a request to fetch listOfThings is already on its way. This is usually done with boolean flags in the state: state.isFetchingListOfThings. The async actions fetching this data cause the reducer to set this flag to true. Your components need to be aware of this and dispatch actions conditionally: if (props.rangeStart !== nextProps.rangeStart && !nextProps.isFetchingListOfThings) { props.fetchListOfThings(); }.

Is it ok to add helper methods to redux store?

I'm making an SVG chart and I have a bunch of helper methods for converting between coordinates and dates. Currently I need to apply scaling everywhere and it's annoying, so I was considering adding the helper methods to the redux store, where the store has access to scaling and can automatically apply it in the methods.
Is this ideal?
I'm also considering creating a function that takes scale, and returns all of the helper methods with the scale curried in. If I do it this way, then I need to reinstantiate this curried function in every file I use it, passing scale each time.
Using redux store I'd only have to do it once.
EDIT: More detail
Restriction: "store" is inaccessible, outside of perhaps middleware.
convert
getDateFromX(x) / scale
to just
getDateFromX(x)
Where scale is built into the function. Like, getDateFromX is always divided by scale, so it should be in the function, but the scale is in the redux store.
I was originally asking if I could have my application reducer return a function in it's returned object "getDateFromX" that could be grabbed through mapStateToProps in connect. I understand it's frowned upon, or I wouldn't have asked the original question, I would have simply implemented this.
Also, there are about 7 more functions that do similar conversions. Converting between hours, days, date, and x.
No. You could technically do that, I guess, but it's definitely not a good use of Redux. (I'm actually having trouble envisioning how "methods attached to the store" would actually fit into things.)
The more idiomatic approach would be to use selector functions. For example:
import {createSelector} from "reselect";
const selectScale = state => state.scale;
const selectSomeValue = state => state.someValue;
const selectScaledValue = createSelector(
selectScale, selectSomeValue,
(scale, somevalue) => scale * someValue
);
If you consistently use selectScaledValue() in your mapStateToProps functions, then it would give you the scaled value every time either the scale or the original value changes.
As our app grows we have been running into similar issues. We try to keep our state as small as possible and then calculate additional information on demand. We've found this to be fairly robust but as the app grows we have to import these state helper functions throughout the app and it's not so user friendly. I've been toying with the idea of taking all the state helpers and attaching them to the state objects in a middleware piece so that components have easy access to them but they aren't actually being stored in our Redux store. I think you could combine markerikson's point but instead of duplicating these functions across your codebase pass them around with the state.
So instead of doing this all over your app
import { getDateFromX } from 'helpers'
...
getDateFromX(state)
You do this:
[ REDUX ] ---> [ CONNECT ] ---> [ UI ]
^
|
Attach helpers here
And then as your state is passed around you can do state.helpers.getDateFromX
This isn't fully fleshed out but I've also been trying to come up with an elegant solution to this issue that doesn't violate best practices of Redux.

What prevents code from changing the store state?

The store has a method called getState that will return the current state of the store.
What prevents code somewhere in my application from (accidentally) modifying the returned state from store?
Let's say i call this:
let state = store.getState();
state.someProperty = 'fun';
The implementation that i've found on getState on the store object simply returns the inner state object that gets overwritten with each new action.
const getState = () => state;
In between actions/new states what prevents code from modifying the state that will be read by another subscriber? In my above example, setting someProperty to 'fun' will persist inside the store on the state property, until overwritten.
While i'm obviously not supposed to modify the state, a simple mistake might bind the state to some component that (unknowingly) modifies its inputs - perhaps on a 2-way binding in an angular environment?
<app-some-component [user]="state"></app-some-component>
Shouldn't getState() be implemented as a clone of its state model?
P.S. This is not specifically related to Angular - which is why i didn't add the tag - to allow more people not used to Angular to answer the question.
The answer is: nothing :)
The core Redux library itself technically doesn't actually care if state gets mutated or not. You could actually mutate in your reducers, or have other parts of your app get the state tree and mutate it, and the store itself wouldn't know or care.
However, mutation will break time-travel debugging, as well as make tests unreliable. Even more importantly, the React-Redux library assumes that you will handle your state immutably, and relies on shallow equality comparisons to see if the state has changed. (This is the reason why "Why isn't my component re-rendering?" is in the Redux FAQ. 99.9% of the time, it's due to accidental mutation.)
If you are concerned about mutation, you can use a library like Immutable.js instead of plain JS objects, or use one of the several tools for freezing your state in development to catch mutations.

Angular-like client side data binding and reactivity with Meteor?

I'm trying to wrap my head around Meteor's way of dealing with reactivity and I want to make sure I've got some concepts right.
Take the follow reactivity example:
A user types something into a form field. The thing that he is typing is instantly displayed somewhere else on the page, as the user is typing, letter by letter. An instantaneous duplication.
From what I know about Angular, this is a very common example of reactivity. Angular binds the input directly to the output on the client side. There's nothing in between.
Correct me since I could be wrong, but Meteor can do this, but the input would first need to be captured and stored into a Mongo + MiniMongo DB (perhaps only as a collection in local storage), there would need to be a subscribe step, and those values would then be read and displayed on the page.
Is there a way to directly bind an event on the front end to another thing on the front end like Angular does?
Is this right? For Meteor to have the front-end-only reactivity of Angular it must first go through the intermediary of a collection, meaning extra code would be necessary to accomplish this compared to Angular?
The example in the Meteor Docs:
Deps.autorun(function () {
Meteor.subscribe("messages", Session.get("currentRoomId"));
});
So here, when the data of currentRoomId changes, the function is reactive to that data change and the function runs (in this case Meteor subscribes to messages).
Using Session variables is the only way I see of possibly binding two parts of a view together directly. Are there other ways?
Meteor's client-side reactivity system (Deps) is not coupled with its live MongoDB syncing. You can use it with any reactive data source which implements the right interface, including data sources which are entirely client-side. For example, you can use the built-in Session object. This is just a client-side key-value store with support for Meteor's reactivity, and you don't have to do any publish or subscribe to use it.
This standard way to do this sort of thing looks something like this:
<input id="field" value="{{fieldValue}}">
Template.form.fieldValue = function () {
return Session.get("fieldValue");
};
Template.form.events({
"input #field": function (evt) {
Session.set("fieldValue", $(evt.currentTarget).val());
}
});
Now the Session variable fieldValue is synced up to the form field. You can call Session.get("fieldValue") in some helper and that template will re-render when the user types in the form field. And if you call Session.set("fieldValue", "blah") then the form field will update itself.
As for your edit: You can make your own reactive data sources using Deps.Dependency, or you could meteor add reactive-dict although that's not documented. There may be packages on Atmosphere.

Resources