I'm trying to use Flux architecture into one of my projects.
Some of my Actions have pre-requisites that need to be satisfied in order to allow that Action to be dispatched.
Currently, this pre-requisite checking logic is inside my View code, something like this (pseudocode):
class FooView {
void OnButtonClick() {
if (FooStore.IsButtonClickAllowed) {
Dispatch(ButtonClickAction);
}
}
}
This looks awkward to me, because now my View has business logic code inside it. I tought about putting this checking code into my Store, but I can't. I have more than one Store that handles this Action, and just one of the Stores knows if it's valid or not. So it won't work:
class FooStore {
void Handle(Action) {
if (Action is ButtonClickAction) {
if (IsButtonClickAllowed) {
FooData.Something();
} else {
// Ignore
}
}
}
}
class BarStore {
void Handle(Action) {
if (Action is ButtonClickAction) {
BarData.Something();
}
}
}
I can't tell from BarStore if the ButtonClickAction is allowed, unless I "WaitFor(FooStore)" and then ask it, but this will cause every Store that handles this Action to contain the same check, leading to something really messy.
So far, I'm understanding that an Action in Flux is only dispatched if it's guaranteed to be allowed, i.e. the validity of the Action needs to be checked before it's dispatched. This means this validation needs to be in the View?
The solution to the case of the button example above may be simple: "just hide the button when it's not allowed and it will never happen". But suppose I have an Action that is dispatched when the user hits the spacebar, what should I do? I can't remove the spacebar from the user's keyboard when the Action is not allowed to happen.
P.S. I'm not using React, so the question is purely about Flux archiectural style, I'm not even using JavaScript.
Perhaps it's a matter of degree. I don't consider a simple check to a property managed by the store to be business logic. I consider that to be very simple view logic:
if (FooStore.IsButtonClickAllowed) {
That line seems to have abstracted away all the rules about why the button might not be clickable, and it looks like all that gets managed in the store, which is appropriate.
an Action in Flux is only dispatched if it's guaranteed to be allowed, i.e. the validity of the Action needs to be checked before it's dispatched.
I disagree with this. There are different kinds of validation.
Sometimes very simple validation can be done in a view component. In React, components may employ a small degree of state. Input components are particularly good places to do this. Checking whether the user has typed in something that looks like an email address, for example, can be done in the view component.
Most validation, however, needs to be done against application state or against persistent data. In these cases, you need to send off the action and then let the stores respond to it with business logic.
The action should be like a newspaper, reporting on something that happened in the real world: the user did something, a response came back from the server, etc. Actions don't need to prevented from happening; they report on what actually happened. Stores do the rest.
When the user does something that violates the validation rules, the stores respond to this by providing error data to the views.
I've created a new component (ActionDispatcher) to handle the validations:
class ActionDispatcher {
void ButtonClick() {
if (FooStore.IsButtonClickAllowed) {
Dispatch(ButtonClickAction);
}
}
}
Then, the views (and other action sources) always uses the ActionDispatcher, instead of dispatching the actions directly:
class FooView {
void OnButtonClick() {
ActionDispatcher.ButtonClick();
}
}
I've just organized the validation code into a single location.
Related
I am setting up my React project with Redux for the first time, and am running into an issue.
I have a basic container, mapStateToProps, action creator, and reducer in place, but I'm having this issue where when I load a certain page, the previous prop values are getting loaded to the page before the correct values are fetched. It makes sense that this is happening, but I was wondering if there was a way to get around this so that upon loading this component / container that the values would get cleared. (Or if it'd just be better to use React state instead of Redux state for this specific use case? The rest of the pages make sense to use Redux, but this is the only case where this becomes an issue)
Say I have a page that shows some state of some item: somePage.com/items/
The component and container are set up like this (skeletal for the sake of example):
class SomeComponent extends React.Component<......> {
constructor(props) {
super(props);
props.dispatch(fetchItemDetail(params));
}
render() {
// use the stuff in props to display item's info
}
}
function mapStateToProps(state) {
return {
someItemDetail: state.someItemDetail.X;
someOtherInfo: state.someDetail.Y;
}
}
export const ItemDetailContainer = connect(mapStateToProps)(SomeComponent)
The action creator involves calling an API and returning the payload, and the reducer is just a standard reducer that sticks the result of the API call into the state.
Basically, this "works", but if I navigate from going into SomeComponent with a parameter for ItemX, and click a link to go to SomeComponent for ItemY, ItemX's information will show until the call to fetch ItemY's info completes, then ItemY's info will appear.
What's the recommended approach for handling this issue? It looks like I can't just clear the props upon construction because they are readonly. Any thoughts?
Self-answer:
I got something to work. Basically, I ended up just creating an action creator, clearItemDetail w/ CLEAR_ITEM_DETAIL type. When the reducer sees actions with CLEAR_ITEM_DETAIL, they would then "clear" that part of the state. (I had multiple things to clear, so my actions and reducers were a little more complicated than this though.)
I dispatched the clearItemDetail() action creator inside the componentWillUnmount() lifecycle method of the component, and it seems to be working now.
Not sure if this is the best route though.
It looks like fetchItemDetail is an asynchronous call. That being so, write a reducer for fetchItemDetail to clear ItemY's data. The reducer can coexist with the middleware to handle the asynchronous call.
case FETCH_ITEM_DETAIL:
return Object.assign({}, state, {ItemY: null});
break;
You can handle the null value in ItemY in your component to not show any data. Then, upon completion of the asynchronous call, assign the returned value (this sounds like it's already done as the ItemY data does appear when the call is complete):
case FETCH_ITEM_COMPLETE:
return Object.assign({}, state, {ItemY: [your item y data]});
break;
I'm converting an existing state model to Redux and it has been painless for the most part. However the one point I'm having trouble with is converting "observed" state ajax requests. Essentially, I have certain ajax requests "linked" to other pieces of state, so no matter who modifies them they'll always be issued correctly. I can get similar behavior by subscribing to the Redux store updates, but firing actions in the listener feels like a hack.
A possible solution is to move logic to the action creator via the thunk pattern. Problem is that I'd either have to duplicate fetching logic across actions (since multiple actions could modify "observed" state), or pull most reducer logic to the action creator level. The action creator also shouldn't be aware of how the reducers will respond to issued actions.
I could batch "sub-actions" so I only need to place the appropriate fetching logic in each action "block", but this seems to violate the concept of actions producing a valid state. I'd rather have this liability at the action creator level.
Are there any generally accepted rules surrounding this? This is not a simple application where ad hoc ajax requests are made as components are interacted with, most data is shared between multiple components and requests are optimized and fetched in reaction to state change.
TLDR;
I want to fire ajax requests in response to changes in state, not when a specific action happens. Is there a better, "Redux specific" way of organizing action/actionCreators to mock this behavior, other than firing these actions in a subscribe listener?
Using store.subscribe()
The easiest way is to simply use store.subscribe() method:
let prevState
store.subscribe(() => {
let state = store.getState()
if (state.something !== prevState.something) {
store.dispatch(something())
}
prevState = state
})
You can write a custom abstraction that lets you register conditions for side effects so they are expressed more declaratively.
Using Redux Loop
You might want to look at Redux Loop which let you describe effects (such as AJAX) calls together with state updates in your reducers.
This way you can “return” those effects in response to certain actions just like you currently return the next state:
export default function reducer(state, action) {
switch (action.type) {
case 'LOADING_START':
return loop(
{ ...state, loading: true },
Effects.promise(fetchDetails, action.payload.id)
);
case 'LOADING_SUCCESS':
return {
...state,
loading: false,
details: action.payload
};
This approach is inspired by the Elm Architecture.
Using Redux Saga
You can also use Redux Saga that lets you write long-running processes (“sagas”) that can take actions, perform some asynchronous work, and put result actions to the store. Sagas watch specific actions rather than state updates which is not what you asked for, but I figured I’d still mention them just in case. They work great for complicated async control flow and concurrency.
function* fetchUser(action) {
try {
const user = yield call(Api.fetchUser, action.payload.userId);
yield put({type: "USER_FETCH_SUCCEEDED", user: user});
} catch (e) {
yield put({type: "USER_FETCH_FAILED",message: e.message});
}
}
function* mySaga() {
yield* takeEvery("USER_FETCH_REQUESTED", fetchUser);
}
No One True Way
All these options have different tradeoffs. Sometimes people use one or two, or even all three of them, depending on what turns out to be most convenient for testing and describing the necessary logic. I encourage you to try all three and pick what works best for your use case.
You can use a middleware to fire up your remote actions in response to the local action.
Let say I have a local action:
const updateField = (val) => {
{type: UPDATE_FIELD, val}
}
And a input field with:
<input type='text' onChange={this.props.updateField.bind(this.val)}>
So in a nutshell when you type inside of the field it fires your action that in turn changes the state via reducer. Lets just forget how this action was passed to the component or what this.val is - we just assume this has been already solved and it is working.
All is fine about this setup but it only changes your state locally. To update the server you will have to fire another action. Lets build it:
const updateFieldOnServer = (val) => {
return (dispatch) => {
MAKE_AJAX.done(
FIRE_SOME_ACTIONS_ON_SUCCESS
).failure(
FIRE_SOME_ACTIONS_ON_FAILURE
)
}
}
This is just an simple thunk async action thats somehow makes ajax request, returns promises and does something else on success or failure.
So the problem we have now is that I want both of this actions to be fired when I change the state of my input but I can't have the onChange to take two functions. So I will create a middleware named ServerUpdatesMiddleware
import _ from 'lodash'
import {
UPDATE_FIELD,
} from 'actionsPath'
export default ({ dispatch }) => next => action => {
if(_.includes([UPDATE_FIELD], action.type)){
switch(action.type){
case UPDATE_FIELD:
dispatch(updateFieldOnServer(action.val))
}
}
return next(action)
}
I can add it to my stack:
import ServerUpdatesMiddleware from 'pathToMe'
const createStoreWithMiddleware = applyMiddleware(
ServerUpdatesMiddleware,
thunkMiddleware,
logger
)(createStore);
And right now every single time when updateField action will be dispatched It will automatically dispatch updateFieldOnServer action.
This is just example I think will describe the problem easily - this problem can be fixed in many other different ways but I think it nicely fits the requirements. It is just how I do things - hope it will help you.
I am using middlewares all the time and have many of them - never had any problem with this approach and it simplifies the application logic - you only have to look in a single place to find out whats going on.
Having modules that subscribe to the state updates and the launch Ajax requests (firing actions as they go) seems fine to me, since it puts the stores/reducers firmly in charge of triggering requests. In my large app, ALL Ajax requests and other async behaviours are done this way, so all actions can be just payloads, with no concept of 'action creators'.
If possible, avoid cascading sync actions. My async handlers never fire actions synchronously, but only once the request completes.
In my view, this is a much more functional approach than async action creators, which you may or may not prefer!
componentWillReceiveProps of react life cycle is the best place to do this. componentWillReceiveProps will be passed both new and old props and inside that you can check for the change and dispatch your action which in turn will fire the ajax call.
But the catch here is state object for which you are checking needs to be added as component's props via mapStateToProps, so that it gets passed to componentWillReceiveProps. Hope it helps!
I am creating my first project that uses ui-router.
My project has about 10 views, each with their own controller and state. I am trying to modularise/encapsulate/decouple as best as possible but I am having trouble working out where to put the onExit and onEnter state callbacks.
The first option is to put it in app.js which is currently defining all of my states, however I feel that this would not be a good place as it could cause this file to blow up and become hard to read as more states are introduced and the logic gets more complex.
The second option I looked into was to put it into a controller (I have one for each state), however from researching it doesn't seem to be best practice to do this.
The third option is to create a service that is resolved, however with this option I would end up with either a giant service full of state change functions for each of the states (not decoupled) or an additional service per state which would contain the state change functionality, and I worry that would increase project complexity.
What is the standard way to achieve this?
Are there any other options that I am missing?
Our strategy for this has been to disregard the onEnter and onExit on the state object, because as you are discovering, they feel like they are in the wrong place in terms of separation of concerns (app.js).
For onEnter: we handle setup in an activate() function in each controller, which we manually execute inside the controller. This happens to also match the callback that will get executed in Angular 2.0, which was not an accident ;).
function activate() {
// your setup code here
}
// execute it. this line can be removed in Angular 2.0
activate();
For onExit: We rarely need an exit callback, but when we do, we listen for the $scope $destroy event.
$scope.$on("$destroy", function() {
if (timer) {
$timeout.cancel(timer);
}
});
In the last three days I've struggled trying to find a way to accomplish what I though was supposed to be a simple thing. Doing this on my own or searching for a solution in the web, didn't help. Maybe because I'm not even sure what to look for, when I do my researches.
I'll try to explain as much as I can here: maybe someone will be able to help me.
I won't say how I'm doing it, because I've tried to do it in many ways and none of them worked for different reasons: I prefer to see a fresh advice from you.
In most of the pages of web application, I have two links (but they could be more) like that:
Option A
Option B
This is partial view, retured by a controller action.
User can select or both (all) values, but they can't never select none of them: meaning that at least one must be always selected.
These links must che accessible in almost all pages and they are not supposed to redirect to a different page, but only to store this information somewhere, to be reused when action needs to filter returned contents: a place always accessible, regarding the current controller, action or user (including non authenticated users) (session? cookie?).
This information is used to filter displayed contents in the whole web application.
So, the problem is not how to create the business logi of that, but how (and where) to store this information:
without messing with the querystring (means: keeps the querystring as empty/clean as possible)
without redirecting to other pages (user must get the current page, just with different contents)
allow this information to persists between all views, until user click again to change the option(s)
My aim is to have this information stored in a model that will contains all options and their selection status (on/off), so the appropriates PartialView will know how to display them.
Also, I could send this model to the "thing" that will handle option changes.
Thanks.
UPDATE
Following Paul's advice, I've took the Session way:
private List<OptionSelectionModel> _userOptionPreferences;
protected List<OptionSelectionModel> UserOptionPreferences
{
get
{
if (Session["UserOptionPreferences"] == null)
{
_userOptionPreferences= Lib.Options.GetOptionSelectionModelList();
}
else
{
_userOptionPreferences= Session["UserOptionPreferences"].ToString().Deserialize<List<OptionSelectionModel>>();
}
if (_userOptionPreferences.Where(g => g.Selected).Count() == 0)
{
foreach (var userOptionPreferencesin _userOptionPreferences)
{
userOptionPreferences.Selected = true;
}
}
UserOptionPreferences= _userOptionPreferences;
return _userOptionPreferences;
}
private set
{
_userOptionPreferences= value;
Session["UserOptionPreferences"] = _userOptionPreferences.SerializeObject();
}
}
Following this, I've overridden (not sure is the right conjugation of "to override" :) OnActionExecuting():
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
GetOptionSelections();
base.OnActionExecuting(filterContext);
}
GetOptionSelections()...
private void GetOptionSelections()
{
if (String.IsNullOrEmpty(Request["optionCode"])) return;
var newOptionCode = Request["optionCode "];
foreach (var userOptionPreferencesin UserOptionPreferences)
{
if (userOptionPreferences.OptionCode == newOptionCode )
userOptionPreferences.Selected = !userOptionPreferences.Selected;
}
}
This code I think can be better, but right now I just want to make it work and it doesn't.
Maybe there are also other issues there (quite sure, actually), but I believe the main issue is that OnActionExecuting is called by each action in a controller that inherit from BaseController, therefore it keeps toggling userOptionPreferences.Selected on/off, but I don't know how to make GetOptionSelections() being called only once in each View: something like the old Page_Load, but for MVC.
Last update AKA solution
Ok, using the session way, I've managed to store this information.
The other issue wasn't really on topic with this question and I've managed to solve it creating a new action that take cares of handling the option's change, then redirects to the caller URL (using the usual returnUrl parameter, but as action parameter).
This way, the option change is done only once per call.
The only thing I don't really like is that I can't simply work with the UserOptionPreferences property, as it doesn't change the session value, but only the value in memory, so I have to set the property with the new object's status each time: not a big deal, but not nice either.
This is a place to use session.
The session will keep your setting between requests while keeping it out of the url querystring. It seems that you have probably tried this already, but try it again and if you have problems ask again. I think it will be the best way for you to solve this problem.
I recently had a problem with multiple form posting in an ASP.NET MVC application. The situation was basically, if someone intentionally hammered the submit button, they could force data to be posted multiple times despite validation logic (both server and client side) that was intended to prohibit this. This occurred because their posts would go through before the Transaction.Commit() method could run on the initial request (this is all done in nHibernate)
The MVC ActionMethod looked kind of like this..
public ActionResult Create(ViewModelObject model)
{
if(ModelState.IsValid)
{
// ...
var member = membershipRepository.GetMember(User.Identity.Name);
// do stuff with member
// update member
}
}
There were a lot of solutions proposed, but I found the C# lock statement, and gave it a try, so I altered my code to look like this...
public ActionResult Create(ViewModelObject model)
{
if(ModelState.IsValid)
{
// ...
var member = membershipRepository.GetMember(User.Identity.Name);
lock(member) {
// do stuff with member
// update member
}
}
}
It worked! None of my testers can reproduce the bug, anymore! We've been hammering away at it for over a day and no one can find any flaw. But I'm not all that experienced with this keyword. I looked it up again to get clarification...
The lock keyword marks a statement block as a critical section by obtaining the mutual-exclusion lock for a given object, executing a statement, and then releasing the lock
Okay, that makes sense. Here is my question.
This was too easy
This solution seemed simple, straightforward, clear, efficient, and clean. It was way too simple. I know better than to think something that complicated has that simple a solution. So I wanted to ask more experienced programmers ...
Is there something bad going on I should be aware of?
No it's not that easy. Locking only works if the same instance is used.
This will not work:
public IActionResult Submit(MyModel model)
{
lock (model)
{
//will not block since each post generates it's own instance
}
}
Your example could work. It all depends on if second-level caching is enabled in nhibernate (and thus returning the same user instance). Note that it will not prevent anything from being posted to the database, just that each post will be saved in sequence.
Update
Another solution would be to add return false; to the submit button when it's being pressed. it will prevent the button from submitting the form multiple times.
Here is a jquery script that will fix the problem for you (it will go through all submit buttons and make sure that they will only submit once)
$(document).ready(function(){
$(':submit').click(function() {
var $this = $(this);
if ($this.hasClass('clicked')) {
alert('You have already clicked on submit, please be patient..');
return false;
}
$this.addClass('clicked');
});
});
Add it do you layout or to a javascript file.
Update2
Note that the jquery code works in most cases, but remember that any user with a little bit of programming knowledge can use for instance HttpWebRequest to spam POSTs to your web server. It's not likely, but it could happen. The point I'm making is that you should not rely on client side code to handle problems since they can be circumvented.
Yeah, it's that easy, but - there may be a performance hit. Remember that a Monitor lock restricts that code to be run by only one thread at a time. There is a new thread for each HTTP Request, so that means only one of those requests at any given time can access that code. If it's a long running procedure, or a lot of people are trying to access that part of the site at the same time - you might start to sluggish responses.
It's that easy, but be careful what object you lock on. It should be the same one for all the threads - for example, it could be a static object.
lock is syntactic sugar for a Monitor, so there is quite a bit going on under the cover.
Also, you should keep an eye out for deadlocks - they can happen when you lock on two or more objects.