I am trying to build a mind mapping application using SolidJS. It shows 'nodes' with content that are organised in a tree, and the application automatically lays the nodes out in a tree-like display.
To perform this layouting, the application must rely on the rendered size of div that 'cascade up' to parent nodes.
The problem I have is that changes in size of 'children' nodes don't trigger the effect on the 'parent' node.
I have simplified my application as much as I could to illustrate the problem and so here is the code:
import { createEffect, type Component, For } from "solid-js";
import { createStore } from "solid-js/store";
let root = {
id: 0,
parentid: -1,
text: "Root",
ownHeight: 10,
children: [],
totalHeight: 10,
};
let initialState = { nodes: [root] };
const [state, setState] = createStore(initialState);
let a = {
id: 1,
parentid: 0,
text: "initial child",
ownHeight: 10,
children: [],
totalHeight: 10,
};
const Node = (props: any) => {
let ref: any;
createEffect(() => {
if (props.parentid === -1) {
console.log("Effect running for parent");
} else {
console.log("Effect running for child");
}
if (ref && props.text) {
setState("nodes", (n) => n.id === props.id, "ownHeight", ref.offsetWidth);
}
if (props.children.length > 0) {
let t = props.ownHeight;
props.children.forEach((element: number) => {
let n = state.nodes.find((i) => i.id === element);
t += n!.totalHeight;
});
setState("nodes", (n) => n.id === props.id, "totalHeight", t);
} else {
setState(
"nodes",
(n) => n.id === props.id,
"totalHeight",
props.ownHeight
);
}
});
return <div ref={ref}>{props.text}</div>;
};
const changeownbox = () => {
console.log("Changing content of child");
setState(
"nodes",
(n) => n.id === 1,
"text",
"This is a text \n on two lines"
);
};
const addChild = () => {
setState("nodes", [...state.nodes, a]);
setState("nodes", (n) => n.id === 0, "children", [a.id]);
};
const App: Component = () => {
return (
<div>
<For each={state.nodes}>{(item) => <Node {...item} />}</For>
<button onClick={addChild}>Add child</button>
<button onClick={changeownbox}>Change child content</button>
</div>
);
};
export default App;
When I run this app sandbox, I get the expected logs in the console:
- "Effect running for parent" when the parent node is first created
- "Effect running for child" when I click 'Add child' and the child is created
- "Effect running for parent" when the child is added to the list of children
- "Changing content of child" when I click 'Change child content'
- "Effect running for child" when the child content is modified But I am NOT getting the 'Effect running for parent' log I would expect at that point.
When the child is created and the effect runs for the parent it reads the totalHeight property of the child, which is an element of the store and should therefore be reactive.
But maybe (newbie question) dependencies are only created on the first run of the effect, and then since the child does not exist at that point, this effect on the parent is not triggered? If so, how could I work around this?