I've been working on a SolidJS application where I store a large JSON object with nested objects. For undo and redo actions, I'm using Immer to generate patches. Although technically I'm storing a class with multiple layers of nesting, Immer handles it seamlessly.
My goal is to pass a subset of the main object to components, such as bigObject.someProp[0]
, allowing the component to modify and access that specific subset instead of accessing bigObject
directly. Here's what I have so far:
function createImmerSignal<T>(value: T) {
const [get, set] = createSignal(value);
function newSet(callback: (draft: Draft<T>) => void) {
const newVal = produce(get(), callback, (redo, undo) => {
// Undo and redo logic, modifying some context variable
});
set(newVal as Exclude<T, Function>);
}
// Looking to implement createNested
return [get, newSet, createNested];
}
type Company = {
name: string;
developers: {
name: string;
someOtherProp: string;
}[];
};
function CompOne(prop: { origCompany: Company }) {
const [company, setCompany, createNested] = createImmerSignal<Company>(origCompany);
// Syntax for createNested:
const dev = createNested(company => company.developers[0]);
return <>
<CompTwo dev={dev} />
</>
}
function CompTwo(props) {
// createNested can be used indefinitely
const [dev, setDev, createNested] = props.dev;
setDev(draft => {
// Updating company.developers[0].someProp
draft.name = 'Darles Nemeni';
});
return <></>;
}
Implementing this in a way that respects types has proven challenging. I've tried using functions (
company => company.developers[0]
) to select a subset of company
:
function newNested<U>(sel: (orig: T | Draft<T>) => U) {
return (fn: (draft: Draft<U>) => void) => set(
produce(get(), draft => {
sel(draft) = produce(sel(draft), draft => fn(draft))
}) as Exclude<T, Function>
)
}
newNested(company => company.developers[0])
However, assigning a value to sel(draft)
proved tricky without accessing a property on the parent first. I'm unsure of a clean solution for this issue.
One potential approach (though uncertain if feasible) could involve using a proxy to track accessed properties, creating a chain of accessors to apply to draft
. Yet, this seems inefficient and messy.
In summary, what's the optimal and neatest way to tackle this design challenge?
Another query: Struggling to articulate this problem accurately, any suggestions for a more fitting title are welcome.