Skip to content

Instantly share code, notes, and snippets.

@alevosia
Last active September 7, 2020 16:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alevosia/9f69a698e6ec7fc253704ca4394f19e0 to your computer and use it in GitHub Desktop.
Save alevosia/9f69a698e6ec7fc253704ca4394f19e0 to your computer and use it in GitHub Desktop.
State time-traveling with React Context API
import React, { useReducer, useCallback, createContext } from 'react'
import id from 'uuid/v4';
import initialState from './initialState';
export const GrudgeContext = createContext()
const GRUDGE_ADD = 'GRUDGE_ADD'
const GRUDGE_FORGIVE = 'GRUDGE_FORGIVE'
const UNDO = 'UNDO'
const REDO = 'REDO'
const useTimeTravelingReducer = (reducer, initialState) => {
const undoState = {
past: [],
present: initialState,
future: []
}
const timeTravelingReducer = (state, action) => {
const newPresent = reducer(state.present, action)
if (action.type === UNDO) {
const [newPresent, ...newPast] = state.past
const newFuture = [state.present, ...state.future]
return {
past: newPast,
present: newPresent,
future: newFuture
}
}
if (action.type === REDO) {
const [newPresent, ...newFuture] = state.future
const newPast = [state.present, ...state.past]
return {
past: newPast,
present: newPresent,
future: newFuture
}
}
return {
past: [state.present, ...state.past],
present: newPresent,
future: []
}
}
return useReducer(timeTravelingReducer, undoState)
}
const reducer = (state = initialState, action) => {
if (action.type === GRUDGE_ADD) {
return [
action.payload,
...state
]
}
if (action.type === GRUDGE_FORGIVE) {
return state.map((grudge) => {
if (grudge.id !== action.payload.id) return grudge;
grudge.forgiven = !grudge.forgiven;
return grudge;
})
}
return state;
}
export const GrudgeProvider = ({ children }) => {
const [state, dispatch] = useTimeTravelingReducer(reducer, initialState);
const grudges = state.present
const hasPast = state.past.length > 0
const hasFuture = state.future.length > 0
const addGrudge = useCallback(({ person, reason })=> {
dispatch({
type: GRUDGE_ADD,
payload: {
person,
reason,
forgiven: false,
id: id()
}
})
}, [dispatch]);
const toggleForgiveness = useCallback((id) => {
dispatch({
type: GRUDGE_FORGIVE,
payload: { id }
})
}, [dispatch]);
const undo = useCallback(() => {
dispatch({ type: UNDO })
}, [dispatch])
const redo = useCallback(() => {
dispatch({ type: REDO })
}, [dispatch])
const value = {
grudges,
addGrudge,
toggleForgiveness,
undo,
redo,
hasPast,
hasFuture
}
return (
<GrudgeContext.Provider value={value}>
{children}
</GrudgeContext.Provider>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment