sketchbook-js / sketchbook

The design tool that lives in your repo.
https://sketchbookjs.com
MIT License
7 stars 1 forks source link

Spike: Investigate using Recoil #116

Open haydn opened 3 years ago

haydn commented 3 years ago

Hypothesis

Using Recoil to maintain the app's state will improve performance (especially for documents that have a lot of layers) and also provide a useful pattern for dealing with nested state.

Deliverables

Put together a CodeSandbox or two to test how Recoil will work with some of the key pieces of functionality.

Specific things to test:

Notes

Mxchaeltrxn commented 3 years ago

@haydn Hey I'm not sure what you mean by this:

Dealing with nested state. What if layers could be organised into groups and those groups are nested recursively, how would we handle that in a Recoil world? (This should also answer the same question for options.)

I get that we want to test dealing with nested state and I can do that by testing how we've implemented the recursion through the options but I'm not sure what you mean by the rest of the statement.

I'm guessing that testing this using the recursion with options should be fine also for the sake of this but I just wanted to make sure.

haydn commented 3 years ago

@Mxchaeltrxn Yeah, we just want to test how we'd handle nested state. I was thinking of a hypothetical case like this might be easier to demonstrate in a minimal example, but feel free use the options if that works better:

const exampleDoc = {
  type: "SketchbookDocument",
  layers: [
    {
      id: "1",
      type: "SketchbookComponent",
      component: "Heading 1",
      name: "Heading 1",
      x1: 100,
      y1: 100,
      x2: 750,
      y2: 140,
      options: { text: "Example Heading 1" }
    },
    {
      id: "2",
      type: "SketchbookGroup",
      name: "Example Group",
      layers: [
        {
          id: "3",
          type: "SketchbookComponent",
          component: "Heading 2",
          name: "Heading 2",
          x1: 100,
          y1: 200,
          x2: 750,
          y2: 240,
          options: { text: "Example Heading 2" }
        },
        {
          id: "4",
          type: "SketchbookComponent",
          component: "Paragraph",
          name: "Paragraph",
          x1: 100,
          y1: 300,
          x2: 750,
          y2: 340,
          options: { text: "Example Paragraph" }
        }
      ]
    }
  ]
};
haydn commented 3 years ago

@Mxchaeltrxn FYI, I'm also doing a similar investigation to see what it might look like if we used Immutable.js here: https://github.com/sketchbook-js/sketchbook/issues/126

Mxchaeltrxn commented 3 years ago

@haydn Hey thanks for letting me know! I've been a bit busy recently but will be back on this on this Wednesday.

Mxchaeltrxn commented 3 years ago

@haydn I've investigated the first 4 parts of this and I'll do the last one a bit later. Just putting this here so you have some time to look at it first.

Visually, it's a bit ugly but I think it does the job. Here is the sandbox.

I didn't use your sandbox (for better or worse) because I felt it was slightly more complicated but I may use it for the postMessage section. Thanks for making it though!

Brief explanation of the UI:

LAYERS
--------------------------------
--------------------------------
Layer 1
options for layer 1
--------------------------------
options for layer 1 (again)
--------------------------------
--------------------------------
Layer 2 ... (repeat of above)

To test:

Updating individual layers without updating the whole document.

  1. Look at the console log and record the time of the last app rerender.
  2. Click Edit layer id button.

Findings: Only the layer that was edited is edited (look at the layer name to check) and the timestamp should correspond to the time that you clicked the button.

Updating options without updating the whole document (just the layer the options belong to should be affected).

  1. Uncomment line 138 to see Option rerenders.
  2. Save and reload the application.
  3. Click Edit option id button for any option.

Findings: Only the option id for the selected option changes (for both lists that I've rendered). Only those two options rerender; the layer and the document don't rerender.

Dealing with nested state. What if layers could be organised into groups and those groups are nested recursively, how would we handle that in a Recoil world? (This should also answer the same question for options.)

I've implemented something similar to what we have in sketchbook (but I've done some hardcoding here).

  1. If you render either the 3rd option in Layer 1 (with "value":["nested 0,0","nested 0,1"]

Findings: array that contains that option rerenders in both the top and bottom options for Layer 1.

Mixing input state with the document state. Things like resizing layers is derived from both the document (for the original layer position) and input (for the mouse dragging details). Would that mean we need to also manage the input state with Recoil, or can we just combine that with the Recoil state as needed?

I didn't do a demo for this because I think I understand how to implement this (if you're still unsure then I can make something because I could be assuming things). My understanding is that we can use selectors to create derived state (which I believe is the general case for what you want when you say you want to combine state1 and state2 to calculate something else). Selector example from the Recoil docs:

const mySelector = selector({
  key: 'MySelector',
  get: ({get}) => get(myAtom) * 100,
});

We can pass in normally handled react state (or just any variables we want) like so:

const mySelector = (NORMALLY_HANDLED_REACT_STATE) => selector({
  key: 'MySelector',
  get: ({get}) => get(myAtom) * 100,
});

I'm unsure of this will have any performance impacts though.

Also this video by one of the creators was a good watch to get to know the Recoil library a bit more