preactjs / signals

Manage state with style in every framework
https://preactjs.com/blog/introducing-signals/
MIT License
3.71k stars 91 forks source link

It is possible to use @preact/signals-react with multiple webpack entries? #379

Closed Alejandroid17 closed 1 year ago

Alejandroid17 commented 1 year ago

Hi, I'm working on a project where we have different React components that are injected into the page individually (multiple webpack entries) and I want to communicate the state between different components (on different entries) but using "signals" the state is not updated.

Some small code fragments:

// webpack-configuration.js
module.export = {
    ...
    entry: {
        'componentA': 'path/component-a-dom.js',
        'componentB': 'path/component-b-dom.js',
    },
    ...
}
// signal.js
import { signal } from "@preact/signals-react";

const count = signal(0);

export default count;
// componentA.js
import count from "path/signal.js";

const componentA = () => {
    return <button onClick={() => count.value++}>My button ({count.value})</button>
}
// componentB.js
import count from "path/signal.js";

const componentB = () => {
    return <div> The number is: {count.value}</div>
}

Is it possible to work with this configuration/code?

Thank you very much!

rschristian commented 1 year ago

Using the Context API (instead of direct import) would likely be the simplest solution, therefore avoiding wrangling Webpack.

Alejandroid17 commented 1 year ago

Using the Context API (instead of direct import) would likely be the simplest solution, therefore avoiding wrangling Webpack.

@rschristian How would this be done? I can't get it to work.

// webpack-configuration.js
module.export = {
    ...
    entry: {
        'componentA': 'path/componentA.js',
        'componentB': 'path/componentB.js',
    },
    ...
}
// my-context.js

import { createContext } from 'react';
import { signal } from '@preact/signals-react';

const countSignal = signal(0);

const CountSignalContext = createContext(countSignal);

export default CountSignalContext;
// componentA.js
import React, { useContext, useEffect, useState } from 'react';
import CountSignalContext from '../../../../my-context';

const componentA = () => {
    const countSignal = useContext(CountSignalContext);
    return <div>{countSignal}</div>;
}
// componentB.js
import React, { useContext, useEffect, useState } from 'react';
import CountSignalContext from '../../../../my-context';

const componentB = () => {
    const countSignal = useContext(CountSignalContext);
    return <button onClick={() => countSignal.value++}>Signal: {countSignal.value}</button>;
}

The values are out of synch (7 - 0)

image

rschristian commented 1 year ago

Have you forgotten to insert the provider higher up in the tree?

For example:

import CountSignalContext from '../../../../my-context';
const mySignal = signal(0);

const App = () => (
  <CountSignalContext.Provider value={mySignal}>
    <ComponentA />
    <ComponentB />
  </CountSignalContext.Provider>
)
Alejandroid17 commented 1 year ago

Have you forgotten to insert the provider higher up in the tree?

For example:

import CountSignalContext from '../../../../my-context';
const mySignal = signal(0);

const App = () => (
  <CountSignalContext.Provider value={mySignal}>
    <ComponentA />
    <ComponentB />
  </CountSignalContext.Provider>
)

@rschristian I'm sorry, maybe I didn't explain myself well from the beginning.

I have multiple webpack entries that are injected individually, so I don't see a way to add the "provider".

Code for injecting components into the DOM.

// component-a-dom.js
const elementID = 'component-a';
const container = document.getElementById(elementID);

const root = createRoot(container);

root.render(
    <ComponentA>
);
// component-b-dom.js
const elementID = 'component-b';
const container = document.getElementById(elementID);

const root = createRoot(container);

root.render(
    <ComponentB>
);

Thank you very much for your time.

rschristian commented 1 year ago

Oh, you don't have common ancestor for your entries?

In that case, you're going to need to wrangle Webpack. Hopefully if you export a separate chunk, both entries will use the same chunk:

// componentA.js
const { default: count } = import("path/signal.js");

const componentA = () => {
    return <button onClick={() => count.value++}>My button ({count.value})</button>
}
// componentB.js
const { default: count } = import("path/signal.js");

const componentB = () => {
    return <div> The number is: {count.value}</div>
}
rschristian commented 1 year ago

Going to close this out, it's probably better suited for Webpack's issues than here.