Как реализовать redo/undo с вложенным redo/undo?
Есть данные в стейте которые приходят в экшене SET_DATA, в редюсере я добавляю новые данные в history.current, старые в past. Формат данных можно поменять если я не правильно начал реализовывать.
При нажатии комбинации клавиш срабатывает экшн changeStateHistory('undo'|'redo'), который заменяет history.current нужным значением.С глобальным историей все нормально работает. Но должна быть возможность откатить не всю историю, а только выбранного агента.
State:
//state example
{
selectedAgent: -1,
history: {
past: [],
current: {
data: [
{
past: [],
current: {
agentId: 1,
agentName: 'Test User1',
future: []
}, {
past: [],
current: {
agentId: 2,
agentName: 'Test User2',
future: []
}
],
isModified: false
},
future: []
}
}
Reducer:
const reducer = createReducer(initialState, {
[ActionTypes.SET_DATA]: (
state: typeof initialState,
action: { payload: { agents: IAgentSchedule[]; isMerge: boolean; isSaveToHistory?: boolean; isModified: boolean } },
) => {
const { agents, isMerge, isSaveToHistory, isModified } = action.payload;
//save changes to history
if (isSaveToHistory) {
state.history.past = clone([...state.history.past, state.history.current]);
state.history.future = [];
} else {
state.history.past = [];
state.history.future = [];
}
if (isMerge) {
state.history.current.data = state.history.current.data.map(agentHistory => {
if (agentHistory.current.isFixLater) return agentHistory;
const foundedAgent = agents.find(_agent => _agent.agentId === agentHistory.current.agentId);
if (foundedAgent) {
return { past: clone([...agentHistory.past, agentHistory.current]), current: foundedAgent, future: [] };
}
return agentHistory;
});
return;
} else {
//initialize history
state.history.current.data = agents.map(agent => {
const agentHistory = state.history.current.data.find(_agent => _agent.current.agentId === agent.agentId);
if (!agentHistory) return { past: [], current: agent, future: [] };
if (!agent.isModified) return { past: agentHistory.past, current: agent, future: agentHistory.future };
return { past: [...agentHistory.past, agentHistory.current], current: agent, future: [] };
});
}
},
[ActionTypes.CHANGE_STORE_BY_HISTORY]: (state: typeof initialState, action: { payload: 'undo' | 'redo' }) => {
const isAgentSelected = state.selectedAgent !== -1;
//change agent history
if (isAgentSelected) {
const agentHistory = state.history.current.data.find(
agentHistory => agentHistory.current.agentId === state.selectedAgent,
);
if (!agentHistory) return;
if (action.payload === 'undo') {
const previous = agentHistory.past[agentHistory.past.length - 1];
if (!previous) return;
agentHistory.past = agentHistory.past.slice(0, agentHistory.past.length - 1);
agentHistory.current = previous;
agentHistory.future = [agentHistory.current, ...agentHistory.future];
return;
} else if (action.payload === 'redo') {
const next = agentHistory.future[0];
if (!next) return;
const newFuture = agentHistory.future.slice(1);
agentHistory.past = [...agentHistory.past, agentHistory.current];
agentHistory.current = next;
agentHistory.future = newFuture;
return;
}
}
//change global history
if (!isAgentSelected) {
const { past, current, future } = state.history;
if (action.payload === 'undo') {
const previous = past[past.length - 1];
if (!previous) return;
state.history.past = past.slice(0, past.length - 1);
state.history.current = previous;
state.history.future = [current, ...future];
} else if (action.payload === 'redo') {
const next = future[0];
if (!next) return;
const newFuture = future.slice(1);
state.history.past = [...past, current];
state.history.current = next;
state.history.future = newFuture;
}
}
},
});
export default reducer;
Источник: Stack Overflow на русском