Skip to content

Instantly share code, notes, and snippets.

@dazld
Created January 9, 2025 14:28
Show Gist options
  • Save dazld/cb38fd65a02a0932403e5b6041e4843a to your computer and use it in GitHub Desktop.
Save dazld/cb38fd65a02a0932403e5b6041e4843a to your computer and use it in GitHub Desktop.
broken mutable reducer like proxy-memoze DO NOT USE
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