Managing state within SolidJS components using Immer's "produce" for nested state handling

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.

Answer №1

One effective approach is to eliminate the usage of createImmerSignal and opt for a store instead: . Utilizing a store grants access to an Immer-like API and leverages a Proxy to manage interactions, promoting reactivity. With various utilities available, managing stored values becomes more seamless, as the reactive nature of the store allows for easy derivation of subsets without added hassle.

If you are set on crafting your own store, examining the implementation can serve as a useful starting point.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Navigating to a specific div within a container with vertical overflow in an Angular application

I am working on an angular application where I have a left column with a scrollable div that has overflow-y: auto;, and a right column with bookmark links that jump to sections within the scrollable container. I am currently facing two challenges: 1 - Co ...

How can you prevent specific dates from being selected in an Angular Datepicker?

Is there a way to exclude Monday from the "mat-datepicker" component? I've tried implementing the following code in my class component: dateFilter = (_date: any) =>{ let day = _date.getDay(); console.log(day); return day != 1; ...

Exploring the Applications of Directives in Multiple Modules within Angular 2

When I declare the directive in two modules, I get an error that says Type PermissionDirective is part of the declarations of 2 modules. However, when I declare it in only one module, I receive an error stating Can't bind to 'isPermission' s ...

Troubleshooting why the Angular innerHTML function is failing to render the specified

I'm encountering this problem where I am receiving a string const str = '<p>Please ensure Process Model diagram represents Functions adequately (boxes that represent an activity or group of activities that produce an outcome):</p>< ...

Unable to import the Node.js array in the import() file

I have encountered an issue while building an array path for the Router.group function. The parameter passed to Router.group is added to the end of the this.groupPath array, but when I check the array data within an import(), it appears to be empty. What c ...

Angular: Extracting a String from an Observable of any Data Type

Currently, I have a backend REST service that is responsible for returning a string: @GetMapping("/role/{id}") public String findRole (@PathVariable("id") String username) { User user = userRepository.findByUsername(username); return user.getR ...

What is the best way to fetch data before a component is rendered on the screen?

I am facing an issue with fetching data from a local server in Node. When I try to render the component, the array 'users' from the state appears to be empty, resulting in no users being displayed on the screen as intended. What's strange is ...

Issues with JSONPATH in typescript failing to grab any values

Searching for a specific config item validity using JSON path can be achieved by specifying the key name condition. This process works seamlessly on platforms like , accurately extracting the desired value: In Typescript, the code implementation would loo ...

Exporting declarations and different export types within a TypeScript ambient module

I am currently working on adding specific types for the config module in our application. The config module is generated dynamically from a JSON file, making it challenging to type. Since it is a node module, I am utilizing an ambient module for the typing ...

Angular does not recognize the boolean variable

Within my Angular component, I have declared two boolean variables: editingPercent: boolean = true; editingCap: boolean = false; In the corresponding HTML file, there is a checkbox that updates these variables based on user input: checkedChanged(e) { ...

Adding to object properties in Typescript

My goal is to dynamically generate an object: newData = { column1: "", column2: "", column3: "", ... columnN: "" } The column names are derived from another array of objects called tableColumns, which acts as a global variable: table ...

The @angular/fire package is unable to locate the AngularFireModule and AngularFireDatabaseModule modules

I am facing some challenges while trying to integrate Firebase Realtime Database into my Angular project. Specifically, I am encountering difficulties at the initial step of importing AngularFireModule and AngularFireDatabaseModule. To be more specific, I ...

Steps for utilizing field labels to transmit values in Protractor

Could someone offer guidance on how to send values using field labels? I understand that it's generally not recommended to use labels for sending values since they can change, but in my case, the labels remain constant. I have attached screenshots of ...

Is it feasible to obtain the userId or userInfo from the Firebase authentication API without requiring a login?

Is it feasible to retrieve the user id from Firebase authentication API "email/password method" without logging in? Imagine a function that takes an email as a parameter and returns the firebase userId. getId(email){ //this is just an example return t ...

Change the class of <body> when the button is clicked

One of my tasks involves adding a button that, when clicked, should give the body the class "open-menu". Implementing this using jQuery was quite straightforward - I just needed to add the following line of code: $('.burger').click(function() ...

Creating Dynamic Graphs using Angular and Chart.js with Array Values

I have implemented ChartJS-2 to visualize a graph displaying an array of user activities, but it appears distorted: import { Component, OnInit, Input } from '@angular/core'; import { ChartOptions, ChartType, ChartDataSets } from 'chart.js ...

Using TypeScript to determine the week number - the value on the right side of the math operation must be of data type 'any'

I've spent a lot of time searching for code to help me calculate the week number in my Angular app according to ISO standards. It's been challenging to find JavaScript-specific code, but I believe I may have found something - however, I encounter ...

Error: Unable to retrieve URL from environment variable UPSTASH_REDIS_REST_URL in the parsing process

Encountering an issue with TypeScript while attempting to create a new Redis instance. I've set up an .env.local file with matching names for the redis URL and token. import { Redis } from '@upstash/redis' export const db: Redis = new Redis ...

"When attempting to render a Node inside the render() method in React, the error message 'Objects are not valid as a React child' is

On my webpage, I have managed to display the following: export class OverworldComponent extends React.Component<OverworldComponentProps, {}> { render() { return <b>Hello, world!</b> } } However, instead of showing Hello, ...

Transform data into JSON format using the stringify method

I am facing an issue with my TypeScript code where I need to retrieve specific information from a response. Specifically, I want to output the values of internalCompanyCode and timestamp. The Problem: An error is occurring due to an implicit 'any&apo ...