Created
January 9, 2025 14:28
-
-
Save dazld/cb38fd65a02a0932403e5b6041e4843a to your computer and use it in GitHub Desktop.
broken mutable reducer like proxy-memoze DO NOT USE
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useReducer, useCallback, useRef } from 'react'; | |
function createMutableReducer(reducer, initialState) { | |
// Track if we're currently in a draft phase | |
let drafting = false; | |
// Store pending changes during the draft phase | |
const changes = new Map(); | |
// Create a proxy handler that tracks mutations | |
const handler = { | |
get(target, prop) { | |
const value = target[prop]; | |
// If the value is an object, wrap it in a proxy too | |
if (typeof value === 'object' && value !== null && drafting) { | |
if (!changes.has(value)) { | |
changes.set(value, new Proxy(value, handler)); | |
} | |
return changes.get(value); | |
} | |
return value; | |
}, | |
set(target, prop, value) { | |
if (!drafting) { | |
throw new Error('State can only be modified inside a reducer'); | |
} | |
// Handle nested objects | |
if (typeof value === 'object' && value !== null) { | |
if (!changes.has(value)) { | |
changes.set(value, new Proxy(value, handler)); | |
} | |
target[prop] = changes.get(value); | |
} else { | |
target[prop] = value; | |
} | |
return true; | |
} | |
}; | |
// Wrap the reducer to handle proxies | |
const wrappedReducer = (state, action) => { | |
drafting = true; | |
changes.clear(); | |
// Create a mutable proxy of the state | |
const draft = new Proxy({ ...state }, handler); | |
// Call the original reducer with the draft | |
const result = reducer(draft, action); | |
drafting = false; | |
// If the reducer returns a value, use that, otherwise use the modified draft | |
return result === undefined ? draft : result; | |
}; | |
return [wrappedReducer, initialState]; | |
} | |
export function useMutableReducer(reducer, initialState) { | |
// Create the mutable reducer setup | |
const [wrappedReducer, wrappedInitialState] = createMutableReducer(reducer, initialState); | |
// Use React's useReducer with our wrapped versions | |
const [state, dispatch] = useReducer(wrappedReducer, wrappedInitialState); | |
return [state, dispatch]; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment