Closed RockNHawk closed 3 years ago
Does this by design ? should not throw exception in reducer ? but put validation logic in reducer is muth cleaner than outsite.
Yes, this is by design. In what occasion would you throw an Exception in a reducer? I've never seen anything like that.
For example, before adding a thing, verify whether it is exists, and throw an exception if it exists.
Well, I can see that but I never ever did that. May you need an Effect to check the existence of the thing and then dispatch a proper action which will not break the pure reducers?
@Odonno
Well, I can see that but I never ever did that. May you need an Effect to check the existence of the thing and then dispatch a proper action which will not break the pure reducers?
I've add a special logic to handle this, I wrap the Redux.Dispatch
method and all the reducer method wrap with an try-catch
, if an Exception
throw in reducer, ·action.Exception· will set, then the wrapped Redux.Dispatch
method whill re-throw the action.Exception
.
A bit ugly, I will use a more elegant way in the future.
interface IWithExceptionAction
{
Exception? Exception { get; set; }
}
static class ActionUtility
{
public static void ThrowIfError<T>(this T action) where T : IWithExceptionAction
{
var ex = action.Exception;
if (ex != null)
throw new WrappedException($"Error execute operate for '{action.GetType().Name}', {ex.Message}", ex);
}
}
public void Dispatch<T>(T action) where T : IWithExceptionAction
{
Store.Dispatch(action);
action.ThrowIfError();
}
public static ReduxSimple.On<TState> OnWithEx<TAction>(Func<TState, TAction, TState> reducer) where TAction : class, IWithExceptionAction
{
return Reducers.On<TAction, TState>((state, action) =>
{
try
{
return reducer.Invoke(state, action);
}
catch (Exception e)
{
action.Exception = e;
return state;
}
});
}
public override IEnumerable<On<FileSystemSourceState>> CreateReducers()
{
return new[]
{
OnWithEx<AddFileSystemSourceAction>(OnAddFileSystemSource),
OnWithEx<StartRemoveFileSystemSourceAction>(OnStartRemoveFileSystemSourceAction),
OnWithEx<DoneRemoveFileSystemSourceAction>((state, payload) =>
{
var oldSources = state.AllSources;
var idx = oldSources.IndexOf(x => x.Id == payload.Source.Id);
** throw Exception: **
if (idx == -1)
throw new InvalidOperationException($"could not find Source#{payload.Source.Id} in state.");
var source = oldSources[idx];
Debug.Assert(source == payload.Source);
source.Status = DataSourceStatus.Removed;
return state.With(
new
{
AllSources = oldSources.RemoveAt(idx),
});
}),
};
}
Hi,
I've add some validation logic in reducer, and throw exception if not valid, but any exception throw will ReduxStore stop work for next action
Dispatch
.It seems the inner Subject set self state to
complete
/error
when exception hanppened in OnNext handler, and never handle newOnNext
call.