-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Reducer is deleting state keys #602
Comments
I can give you a concrete example of one of my middlewares: type Dependencies<S> = {
extractState: (getState: () => S) => CycleState;
extractCurrentCharacters: (getState: () => S) => Character<'current'>[];
};
export const cycleMiddleware: <S>(deps: Dependencies<S>) => Middleware = ({
extractState,
extractCurrentCharacters
}) => api => next => {
const getCharacter = (characterId: string): Character<'current'> => extractCurrentCharacters(api.getState)
.find(c => c.id === characterId)!;
const getNextCharacterInfos = (currentCharacterId: string, index: number = 0): {
id: string;
duration: number;
} | null => {
const { globalTurnOrder } = extractState(api.getState);
if (index === globalTurnOrder.length) {
return null;
}
const orderIndex = globalTurnOrder.indexOf(currentCharacterId) + 1;
const id = globalTurnOrder[ orderIndex % globalTurnOrder.length ];
if (isCharacterDead(id)) {
return getNextCharacterInfos(id, index + 1);
}
return {
id,
duration: getCharacter(id).features.actionTime
};
};
const differTurnStart = (turnSnapshot: TurnSnapshot) => {
const delta = turnSnapshot.startTime - Date.now();
cancelTimeout();
timeout = setTimeout(() => {
const currentCharacter = extractCurrentCharacters(api.getState)
.find(c => c.id === turnSnapshot.characterId)!;
api.dispatch(BattleStateTurnStartAction({
turnSnapshot,
currentCharacter
}));
differTurnEnd(turnSnapshot.characterId, turnSnapshot.startTime);
}, delta);
};
const differTurnEnd = (characterId: string, startTime?: number) => {
cancelTimeout();
const character = getCharacter(characterId);
const endTime = startTime
? startTime + character.features.actionTime
: Date.now();
const duration = endTime - Date.now();
timeout = setTimeout(() => {
api.dispatch(BattleStateTurnEndAction());
const { turnId, currentCharacterId } = extractState(api.getState);
const nextChar = getNextCharacterInfos(currentCharacterId);
if (nextChar) {
differTurnStart({
id: turnId + 1,
startTime: endTime + TURN_DELAY,
characterId: nextChar.id,
duration: nextChar.duration
});
}
}, duration);
};
const turnQueue: TurnSnapshot[] = [];
let timeout: NodeJS.Timeout | null = null;
const cancelTimeout = () => {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
};
return (action: AnyAction) => {
next(action);
if (BattleStartAction.match(action)) {
const { globalTurnSnapshot } = action.payload;
differTurnStart(globalTurnSnapshot.currentTurn);
} else if (ReceiveMessageAction.match(action)) {
const { payload } = action;
if (payload.type === 'battle-run/turn-start') {
const state = extractState(api.getState);
const { id, characterId, startTime } = payload.turnState;
if (state.turnId === id) {
if (state.turnStartTime !== startTime) {
differTurnEnd(characterId, startTime);
}
} else if (id === state.turnId + 1) {
if (getTurnState(state) === 'ended') {
differTurnStart(payload.turnState);
} else {
turnQueue.push(payload.turnState);
}
}
}
}
};
}; I'm sure I'm doing something wrong on my middlewares, but I don't know what.. |
That should not happen :/ On logging: You can log the state by doing Couls you please check which version(s) of |
I've seen one or two other reports like this in the past. Not sure where they are in the issues here, but something like this has been reported. Any chance this is an Electron app of some kind? I feel like that was involved the one time. |
I added
Doing
It seems that
Nop, it's a regular web app, initialized using I found a very strange behavior. Since the issue is always present using the classic Thanks for your time 👍 |
Hmm. Yeah, I remember seeing that Immer mismatch in a prior issue. See #459 and facebook/create-react-app#8750 . And yeah, the hoisting of Immer v1 here is likely causing the issue somehow. |
I edited my package.json to resolve immer version:
After a On the app, the console.log that showed But the issue is still there, Do you think that there is a version mismatch on another dependency ? -- |
At this point I'd need to get hands-on with a project that reproduces the issue, and step through the entire process one line at a time to see where things are breaking. I understand it may be hard to provide a repro for this. If you can't, all I can suggest is stepping through things yourself. |
I did not succeed to make a repro of this issue. function createReducer(initialState, mapOrBuilderCallback) {
var actionsMap = typeof mapOrBuilderCallback === 'function' ? executeReducerBuilderCallback(mapOrBuilderCallback) : mapOrBuilderCallback;
return function (state, action) {
if (state === void 0) {
state = initialState;
}
var caseReducer = actionsMap[action.type];
const shouldLog = action.type === '...';
if(shouldLog) {
console.log('state infos', state, isDraft(state));
}
if (caseReducer) {
if (isDraft(state)) {
// we must already be inside a `createNextState` call, likely because
// this is being wrapped in `createReducer`, `createSlice`, or nested
// inside an existing draft. It's safe to just pass the draft to the mutator.
var draft = state; // We can aassume this is already a draft
return caseReducer(draft, action) || state;
} else {
// @ts-ignore createNextState() produces an Immutable<Draft<S>> rather
// than an Immutable<S>, and TypeScript cannot find out how to reconcile
// these two types.
return createNextState(state, function (draft) {
if(shouldLog) {
console.log(
'state', state, isDraftable(state),
'draft', {
draft, draftSpread: {...draft}, draftOriginal: original(draft)
}
);
}
return caseReducer(draft, action);
});
}
}
return state;
};
} When my issue occurs:
I still don't understand why this issue occurs on middleware dispatch, but since that on the |
Ok finally it's a V8 bug, I found this thanks to this issue immerjs/immer#346 ! I was testing my app on Chromium v73, the bug is fixed since v74 😄 Thank you to have take the time to help me 👍 |
Well, there's one you don't see every day! Appreciate you taking the time to dig through this :) |
Hi,
I have a very strange issue.
Sometimes, after dispatching some actions, I have some keys of my state deleted.
I encounter this issue in a quite big project after passing it to redux/toolkit, so to be understable I show you a simple example:
Middleware
I have a middleware that can dispatch
MyAction
action, async:Reducer
I have my reducer, created with
createReducer()
:The issue happens on the
MyAction
dispatch.redux-logger shows me:
{a: 1, b: 1, c: 1}
{c: 5}
In the reducer, the
...state
seems to be ignored. When I want toconsole.log
the received state I obtain a Proxy object, it does not give me useful information.Also, if instead of returning the new state I mutate the proxy, like that:
state.c = 5
, it works.This issue seems to happen only when I dispatch an action from a middleware. When I do that it's always asynchronous, sometimes deffered of several seconds.
It happens only in reducers returning a new state using spread
...state
.I'm using redux/toolkit 1.3.6, with default middlewares.
I do not succeed to reproduce the issue with a basic example, I'm quite lost on why it happens on my project so any advice on how to debug that is welcome !
The text was updated successfully, but these errors were encountered: