-
Notifications
You must be signed in to change notification settings - Fork 87
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create store bug #52
Comments
I should add that I only call this a bug because it differs from the current implementation. The behavior that redux.net has makes more sense than redux IMO and there is interesting discussion around it in the link. |
Sorry to answer this late. I gave a hint about this in another issue :
What do you think ? If you think it's a good feature to add, PR is welcome :) |
Are you suggesting that we require the reducer(s) to specify the initial state? How exactly would this work in practice? |
@cmeeren I think the way it works for redux is that the smaller reducers can provide their initial state if one is not provided to them. You could call const store = createStore(rootReducer) without passing any initial state and each of the smaller reducers would supply its own. That being said I don't know if that is as necessary for C# because of the OOP style. Each state slice can have a constructor that initializes arrays or other reference types. in js reducers look like: // todos reducer
function todos(state = [], action {
switch(action.type) {
case ADD_TODO:
return [
...state,
todo(null, action)
]
}
}
// todo reducer
const initialState = {
isCompleted: false
}
function todo(state = initialState, action) {
switch(action.type) {
case ADD_TODO:
return {
...state,
text: action.text
}
}
} which simulates the constructor of a particular object that is used for the state. Given that, I didn't know if that initial action was needed or not - I just saw the different behavior of the end result of the initial state of the store. |
I don't think reducers should specify the initial state, because that would preclude them from using the "sentinel" value, e.g. Currently (not saying this is the best solution) I define the initial state when the app starts, in the DI composition root, where I have access to dependencies like app storage etc. The state is initialized through the different state constructors: /// <summary>Provides the initial state of the app.</summary>
public class InitialStateProvider
{
private readonly IPreferencesRepository preferences;
public InitialStateProvider(IPreferencesRepository preferences)
{
this.preferences = preferences;
}
/// <summary>Returns the initial state of the app.</summary>
public RootState GetInitialState()
{
var state = new RootState(
authState: new AuthState(
token: this.preferences.Token,
tokenIsValid: false,
isSigningIn: false,
previousUsername: this.preferences.PreviousSignedInUsername),
projectState: new ProjectState(
projects: ImmutableList<Project>.Empty,
selectedProjectId: null,
isRefreshingProjects: false,
isCheckingInOutProject: false));
return state;
}
} |
@cmeeren I think we are saying the same thing, but Imagine though that you have 5 or 6 root states that are all composed of objects with 5 or 6 other state objects. You can see how this can grow exponentially and be quite a nightmare to maintain all in one place. |
It seems you understand the issue correctly but just to illustrate : public class State
{
public AState A { get; }
public BState B { get; }
}
public static AState ReduceAState(AState state, object action)
{
if(state == null)
return InitialAState();
[...]
}
public static BState ReduceBState(BState state, object action)
{
if(state == null)
return InitialBState();
[...]
}
public static State Reduce(State state, object action)
{
return new State(
ReduceAState(state.A,action),
ReduceBState(state.B,action));
} All the philosophy is explained here. It's not as practical as js because of the optional parameter in first position. @cmeeren You are right that it does not allow "sentinel" values. This pattern is recommended but not mandatory. The developer can still provide an initial state and ignore the "InitAction" will not trigger anything. |
Oh, I see - it's only if the actual state objects are null that the default state (for that state object, e.g. To clarify - if your state tree is like this:
then Have I understood correctly? |
You understood perfectly! :) |
While I was looking at the thread safety test I came across a bug. If you add the below it will fail.
It could be how it was intended to be designed but redux is implemented with an initial action that is dispatched to call all reducers once when the store is created. below is a similar test for redux and when added to the test suite it fails.
The result being that the current state is 1 and not 0 because of the initial dispatch.
interesting discussion about this in the redux issues
The text was updated successfully, but these errors were encountered: