concur-js
concur-js copied to clipboard
Sharing State Between Components
I see the "Good Boy" throttle example. I feel like it would be possible to use a similar technique to listen for events from other components. In this way, I feel I could essentially share state between components by extracting it from events.
Is there a different recommended approach to sharing state between components?
I don't have a good idiomatic solution to inter-component state sharing yet. I suppose in Javascript you could simply use shared variables but that's not great.
Do you have a usecase where you would want to use it? I feel the way forward would be to analyse lots of usecases to come up with a good solution.
I guess I don't have a use case. I do have a perceived convenience issue. Granted, experimenting with the framework for the first time yesterday, I got the sense that "simply use shared variables" might work surprisingly well.
I pseudo-coded this conceptual solution using an in editor linter as an aid:
class ExhaustiveReturns {} // placeholder, helps insure thorough paths through functions
type StoreOutput<T> = [
T, // getter
(t: T) => ExhaustiveReturns // setter
];
// shareable state
type Store<T> = StoreOutput<T>;
// converts tuple to tuple of "Store"s
type MapToStore<T> = { [K in keyof T]: Store<T[K]> };
type ComponentInput<Tuple> = { stores: MapToStore<Tuple> };
// a component type that has access to "Store"s on each render
type Component<Tuple> = (input: ComponentInput<Tuple>) => ExhaustiveReturns; // placeholder type for JSX return
type BuildStoreInput<T> = { initialValue: T };
// initializes a value you wish to share
function buildStore<T>(input: BuildStoreInput<T>): Store<T> {
throw new Error("not implemented");
}
type BuildComponentInput<Tuple> = {
stores: MapToStore<Tuple>;
component: Component<Tuple>;
};
// gives a component access to the "Store"s it needs
function buildComponent<Tuple>(
input: BuildComponentInput<Tuple>
): Component<Tuple> {
throw new Error("not implemented");
}
// example ...
const storeNumber = buildStore<number>({ initialValue: 1 });
const storeString = buildStore<string>({ initialValue: "2" });
const storeBoolean = buildStore<boolean>({ initialValue: false });
const Component0 = buildComponent<[]>({
stores: [],
component: (_stores) => {
throw new Error("not implemented");
},
});
const Component1 = buildComponent<[string, boolean, number]>({
stores: [storeString, storeBoolean, storeNumber],
component: ({
stores: [
[valueString, setString],
[valueBoolean, setBoolean],
[valueNumber, setNumber],
],
}) => {
// silly console.log usage in render
console.log(
valueString,
setString,
valueBoolean,
setBoolean,
valueNumber,
setNumber
);
throw new Error("not implemented");
},
});
const Component2 = buildComponent<[boolean, string]>({
stores: [storeBoolean, storeString],
component: ({
stores: [[valueBoolean, setBoolean], [valueString, setString]],
}) => {
// silly console.log usage in render
console.log(valueString, setString, valueBoolean, setBoolean);
throw new Error("not implemented");
},
});
I have been struggling to merge what I know with the style I see from your framework.
So, this is what I think it really boils down to:
To share state between components, you must re-render the new state. You can still use async generators to yield components, but the yielded component needs to maintain the ability to re-render between yields.
So, if concur-js chooses to yield React components, concur-js can control program flow while React programmers continue to share state between components in whatever way they already do.
But since concur-js should have access to the same state as its React components do between yields, it might as well interop with Redux (popular even if I don't like it) and other various state management libraries as much as possible.