DEV Community

Cover image for 🧿 Turn React into SolidJS, update on demand, no more re-render
ε—ε°εŒ—
ε—ε°εŒ—

Posted on

🧿 Turn React into SolidJS, update on demand, no more re-render

1. What is SolidJS?

We all know SolidJS, if not, why are you reading this article!

We all know SolidJS, here is its doc: https://www.solidjs.com

Put it simply, SolidJS is the real "react" version of React, which is completely updated on demand, and where data changes are updated.

For example, a component:

function App() {
  const [value, setValue] = useState(0);
  return <div>{value}</div>;
}
Enter fullscreen mode Exit fullscreen mode

React calls the entire function of App to death (i.e. re-render), while SolidJS only updates the small piece of value.

Of course, SolidJS is like this:

function App() {
  const [value, setValue] = createSignal(0);
  return <div>{value()}</div>;
}
Enter fullscreen mode Exit fullscreen mode

In SolidJS, App is called only once during initialization, and is not executed after that.

So JSX in SolidJS is equivalent to "static template", which is only used to describe the UI and will not be called again, and there is no diff.

That is to say, any function executed in App or any function executed in JSX will only be triggered once.

2. First declare

How to turn React into SolidJS?

Certainly not rename solid-js to react, nor manually use DOM API to update it without React logic.

It must be stated here:

The following implementation is completely based on the React API, rather than hacking with the DOM API or jQuery, which would be meaningless.

3. How to implement it?

1. How to update only the small piece of value()?

This is the core of the implementation idea, let's just say it - that is to turn value() into a component.

Yes, it displays data, but it's actually a component. It's a component that just returns data.

2. Why value() instead of value?

Because we need to know that there is a data here, and it has to be updated later, how do we know?

According to JS syntax, there is no other way than state.value (use getter) or value() (call function).

This is why SolidJS must be written as value(). If it is written as value, God don't know how to update it, because in the implementation of "static template", the function will not run again.

3. Implement a useSignal similar to createSignal

We want to implement a useSignal, similar to SolidJS' createSignal, which returns two functions, a getter and a setter.

At the same time, the return of the getter is a component.

function useSignal(val) {
  const valRef = useRef(val);
  const update = useRef();

  const Render = () => {
    const [value, setValue] = useState(valRef.current);
    update.current = setValue;
    return value;
  };

  const getter = () => {
    try {
      useState(); // Use this hack to know whether the data is in JSX or read normally elsewhere
      return <Render />;
    } catch (e) {
      return valRef.current;
    }
  };

  const setter = (newVal) => {
    valRef.current = newVal;
    update.current(newVal);
  };

  return [getter, setter];
}
Enter fullscreen mode Exit fullscreen mode

The above is a minimal implementation, but it is problematic because the data may be used in multiple places, and the above can only update the data in the last place.

4. Data synchronization update version useSignal

Collect the update functions with an array of listeners and that's it. In fact, this is also the implementation idea of React state managers.

function useSignal(val) {
  const valRef = useRef(val);
  const listeners = useRef([]);

  const Render = () => {
    const [value, setValue] = useState(valRef.current);

    useEffect(() => {
      listeners.current.push(setValue);
      return () => {
        listeners.current.splice(listeners.current.indexOf(setValue), 1);
      };
    }, []);

    return value;
  };

  return [
    () => {
      try {
        useState();
        return <Render />;
      } catch (e) {
        return valRef.current;
      }
    },
    (payload) => {
      listeners.current.forEach((listener) => {
        listener((prev) => {
          valRef.current =
            typeof payload === 'function' ? payload(prev) : payload;
          return valRef.current;
        });
      });
    },
  ];
}
Enter fullscreen mode Exit fullscreen mode

The above is already a working implementation.

At this point, the core of the story has actually been told.

But if it is to be really used for development needs, there is still a lot of unfinished business.

4. What else is there to do?

If it's really "available", it should at least implement:

  • createEffect (for listening for data updates)
  • createMemo (for creating computed data)
  • onMount (for sending requests)
  • onCleanup (for unsubscribing)
  • What if the data is an object or an array? (This is the most complicated, the above actually only considers primitive data types)
  • How to implement conditional operator or function calls in JSX? (The conditional operator or function is only executed once during initialization and cannot respond to change)
  • How to respond to HMR? What if the data doesn't appear in JSX for the first time? How to unsubscribe after component unmount...

5. Introducing solid-react

There are a bunch of questions written on it, and naturally the answer is ready... This answer is called solid-react.

All the problems mentioned above have been solved. If you have a deeper understanding, you can look at the source code.

☞ GitHub: https://github.com/nanxiaobei/solid-react

Here is the API for solid-react:

  • useSignal (corresponding to createSignal, used to create data)
  • useUpdate (corresponding to createEffect, used to monitor data updates)
  • useAuto (corresponding to createMemo, used to create computed data)
  • useMount (corresponding to onMount, used to send requests)
  • useCleanup (corresponding to onCleanup, used to unsubscribe)
  • the data is an object or an array (use a proxy to handle this tricky case)
  • Run (for conditional operator or functions in JSX, Run(() => fn(value()))

Please pay attention to the naming of the API, which is also said: try not to conflict with existing APIs (such as not directly naming useState useMemo, which will confuse the code), while keeping it concise enough (easy to write) and intuitive (easy to understand).

For specific API introduction, please check README: https://github.com/nanxiaobei/solid-react

In this way, most common development scenarios can already be covered, that is, it can be used for "production".

6. Try solid-react

Demo: https://codesandbox.io/s/solid-react-rymhr6?fontsize=14&hidenavigation=1&theme=dark&file=/src/App.js

Here is a demo, you can open the console, click the button to try, and you will find:

Components don't re-render anymore, React is completely SolidJS-style on-demand updates!

useUpdate useAuto does not need anything like deps, its dependencies are automatically learned. And only when dependencies change, they execute again.

Yes, that is to say, you can get rid of Hooks, useCallback useMemo deps memo, will it trigger re-render, it's all unnecessary.

A function is a function, an object is an object, and it will not be created again if it is written there.

7. What else?

solid-react is an experimental project, just to implement an idea, and in fact it is not bad.

solid-react tries its best to make it "completely capable", whether it is sending requests or monitoring data, sparrows are small (but delicious) and have all the internal organs.

solid-react is a small thing, it may have flaws, of course it can't compare with the maturity of developing directly with React, and it is impossible to compare.

solid-react is definitely fine for small demo projects, but I haven't practiced it in big projects, it's good to play with it first, if you're interested.

solid-react is more like a concept. It is impossible for React officials to take this road, but thanks to the open source, you can experiment on this road yourself.

solid-react works hard to "suffer from Hooks", a general confusion in the industry that has not disappeared for several years (although I feel Hooks are fine)

solid-react welcomes those who are interested to try it together and create more possibilities.

Turn React into SolidJS, say goodbye to Hooks, say goodbye to re-render ↓↓↓

https://github.com/nanxiaobei/solid-react

Some people may want to say, isn't the API of solid-react are Hooks? How to say goodbye to Hooks! In fact, the above is to be compatible with the mixed use of React and solid-react ... Yes, I even considered this situation πŸ™ˆ

Top comments (0)