Get Call Depth Inside React Component - reactjs

I'm trying to make a scene graph in React that supports nesting components. Something along the lines of:
<SceneNode>
<SceneNode>
<Thing/>
</SceneNode>
<SceneNode>
<Thing/>
</SceneNode>
</SceneNode>
When a SceneNode is mounted I need a way for it to know at what "depth" it is. Is there a way within a component to access that information? e.g.:
function SceneNode({children}:{children:ReactNode}) {
const depth = useCallDepth(); // <- some magic function that lets me know the level of nesting for this node
return <div>{children}</div>
}
NOTE: I've tried the following:
Context: Can't get it to work without having each node create its own context but since context is referenced statically in React I can't access the dynamically created parent contexts from children.
Refs: The nesting of components should be reflected in the DOM elements they produce, but I can't figure out how to determine what the associated component is for a given DOM element - i.e. is this div a SceneNode or just a random div? (I suppose I could encode it in an attribute or id, but I was hoping for something a little cleaner and less invasive)
Manual: I can manually specify which nodes are parents (e.g. <SceneNode parent={true}/> but that's pretty error prone.

It is possible to use context for this: Demo

Related

Why is using React Context better than just passing around a props object?

I've been reading about the advantages of using Context in React and I am unconvinced. I'm wondering if there's something I've missed.
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
What's the hassle in creating a props object in the main component and just passing it around among the underlings? Something like:
// do this once at top level (I'm assuming [foo, foo_set] and [bar, bar_set] are state variables):
const props = {foo, foo_set, bar, bar_set, thisAndThat, theOther, whatever, etcAndEtc}
// including one component
<MyComponent1 {...props } />
// including another
<MyComponent2 {...props } />
(Maybe better to use another name than props for this object, as the components can have other properties. Anyway.)
Then in MyComponent1 you can access all the props you want, or not access them. Either:
...
const MyComponent1 = (props) => {
...
// here we can use any props we need, props.bar, props.bar_set, props.theOther for example
const localVar = props.bar * 2;
props.bar_set(localVar);
// this changes the value of bar throughout the app
...
}
the advantage of the above, as I see it, is that you can pass around the props object to other sub-sub-components and not worry about whether you have anything missing.
Or:
...
const MyComponent1 = ({bar, bar_set, theOther }) => {
...
// here we can use bar, bar_set, theOther in the same example
const localVar = bar * 2;
bar_set(localVar);
...
}
The advantage of this option being that the syntax is shorter.
So my point is why not just use the standard JavaScript syntax? Why introduce new concepts when there are plenty to assimilate to do all sorts of other things?
Consider a fairly common case for most applications: You have authentication information (eg, the current user), a routing library (eg, react-router), and a theme object (what colors to use). These are needed in components scattered throughout the app.
You want to render a button somewhere down at the tip of the component tree. It's going to show the user's avatar, so it needs the authentication data. It's going to navigate when clicked, so it needs the navigate function from the routing library. And it needs to style itself according to the theme.
This certainly can be done through props, but in order for the button to get the props, every component in the chain above it must get and forward those props too. This could be many components deep, like page component -> section component -> table -> row -> widget -> button, and most of them don't need that information for themselves, so they're just taking the props in order to forward it along.
And you can easily imagine cases where there are more than 3 pieces of data that are needed across the app.
What's the hassle
Most people find this "prop drilling" to be a hassle, but let's assume you don't. You still have the problem that it has bad performance. If every component must receive the full set of "global" values that the app might need, then any time anything changes, the entire app must rerender. Optimizations like react.memo become effectively useless. You will get much better performance if you only pass the props you need.
Easier to edit code (You don't have to delete for example unused variable)
Better redability (You dont see unnescesary variables, and You see which component is using variables)
Lesser performance waste (preventing from consuming unnescesarry variables)
Suppose You got 10 descendants in - You would have to pass one variable through 10 of components.
What if some could have the same variable name ? You would have to edit Your passed variable for a while, then edit back later.
To sum up:
Using Context more efficient than stuffing everything into a single object variable, because it avoids re-rendering the whole app when anything changes.
People think passing a single variable around is more hassle than introducing specific syntax.
Context also allows you to have different values for the same variable in different parts of the app. This is shown here (the best explanation IMHO) : https://beta.reactjs.org/learn/passing-data-deeply-with-context
The above article also specifies that sometimes passing props is the best solution. It gives a list of use cases for context, and the advantages provided in each case.

Multiple JSX elements with react

If we want to write multiple JSX elements we must wrap it with parent element or React Fragment element to be rendered successfully , Is there any way to write multipe jsx elements without need to wrap it with any parent or fragment element (like maybe make the fragment by default behind the scene without need to write it ) ,
function CustomBtn() {
return (
<h1>CustomBtn</h1>
<h1>CustomBtn</h1>
)
}
i know this will give Parsing error: Adjacent JSX elements must be wrapped in an enclosing tag
i know that it's useless , because we can just use react fragment , but it's like a training task to dig deeper into react configuration
i think in babel , webpack or react dom packages but i can't solve it , any ideas ?
Thanks
This is a good idea. But this is not customizable for the following reason.
React team made the interface of a component to be a single ReactNode. But for some functions, they also allow for an array of nodes, ReactNode[]. To be honest, internally they do support array, otherwise you won't be able to do {arr.map(v => <div />).
Therefore this is not something that you like or not, it's the interface definition of a React component. Imagine you design the component, you can choose to accept an array or a single. If you were the author, you could make that happen ;)
NOTE:
deep down there's another reason, each component accepts a ref. The ref is the DOM element reference, so a single mapping from one ref to a single React node is a more straightforward implementation.
You can return array:
function CustomBtn() {
return [
<h1>CustomBtn</h1>,
<h1>CustomBtn</h1>
]
}

ReactJS wrap children, and descendents, see-through a component

I'm trying to find a good way to produce a layout control that wraps all it's DOM children, let's say in a div tag for simplicity. The basic approach is to use React.Children.map, but this is posing some problems with constructed lists of children via components -- it cannot see inside Components.
For example, say I have this structure, where the final DOM result should be a <ul> with several <li> elements. The Item* components themselves do not know they are in a list.
<WrapItems>
<ItemA/>
<ItemB/>
<ItemC/>
<ItemD/>
</WrapItems>
I can produce the DOM result I want by iterating the react children and wrapping them in a li. However, some of these items are shared between several controls, motivating me to create a common component.
<WrapItems>
<ItemA/>
<CommonItems/>
<ItemD/>
</WrapItems>
Where CommonItems ends up rendering:
<>
<ItemB/>
<ItemC/>
</>
The issue is that I cannot wrap ItemB and ItemC anymore, since React.Children will walk iterate the immediate children*.
Essentially I want CommonItems to be transparent and expose it's children directly to the WrapItems control, so that it can wrap them correctly. Is there a way to do this?
I've tried an approach where I replace the component with a useCommonItems function that returns an array of items instead. In some cases this can work, but in others it becomes a challenge, and a performance concern. Some of these parts are conditional, and the use approach forces them to all end up non-conditionally evaluated, along with all their use... functions in turn.
*Note, I'm aware that React.Children.map does not iterate over fragments, but I can solve that part by doing my own Children.map that descends into fragments. The issue in this question is about iterating over the children of components. The solution may be related, I'm not sure.

How does React provide independent contexts for subtrees?

If, as a thought experiment, I were to write my own createElement implementation for JSX, what might support for implicit context look like?
In particular, I can't figure out how with the limited means of JSX's createElement signature, contexts can be independent for different subtrees. (It appears React's Context handling has become more elaborate in recent versions; I'm mostly interested in the seemingly more straightforward mechanisms of earlier versions.)
This might be used to automatically determine heading levels, for example:
<Section title="Hello World">
<Card title="Details" />
</Section>
<Card title="Example" />
Here Card would automatically generate <h3> and <h2>, respectively, by relying on something like context.headingLevel.
A very nice question, that shows how different is the concept of creating React Elements to actually executing the render functions (either the .render method of class components or simply the main body of a functional component).
In JSX itself (which is just React.createElement(…)) there‘s no concept of “context” at all. It comes into existance only when the components are rendered. It is indeed a duty of the React Renderer (such as React DOM or React Native) to actually implement Context APIs.
If you remove the ability to store states and to update the UI you are left with a minimal React implementation that only “renders once”, but perfectly fine to understand the problem at hand.
Everytime the React Renderer needs to render a React Elements tree (such as one built with JSX) it passes every single element and transforms it into a DOM structure, but when it encounters a component node (not a “native” element) it needs to render it to obtain its React Element sub tree, and swap the original node with it.
It’s in this specific moment that React can keep track of which Context values to pass to which components, since it is traversing the tree.
So, to answer directly your question, you can’t implement context in the “element creation phase”, inside your JSX implementation, you need to do it in a subsequent phase when you can traverse the tree.
If you were trying to build an “immediate JSX” you probably have something like this:
function createElement(type, props, ...children) {
props = { children, ...props };
if (typeof type === 'function') {
return type(props);
} else {
return { type, props };
}
}
In thise case you will not be able to implement an API similar to context, because the execution order is inner-then-outer:
const div = createElement('div', {}, createElement(Card, {}));
// identical to
const card = createElement(Card, {}); // inner, and then…
const div = createElement('div', {}, card); // outer

React in-memory Portal

I do want to create a React component tree in memory (not in the DOM) with the same behavior a React Portal has, that is, it should use contexts from the parent component tree.
So is it possible to create something like Portal but instead of render it on a DOM node, "render" it in memory (outside the DOM)?
Note that what I need is the component life-cycle of some components in the tree while the visual part will be hidden, but in my case if I put the component tree in the DOM within a hidden div the performance is not good and then I want to test a different approach.
Two possibilities come to my mind:
1) create a portal to a DocumentFragment
When you use ReactDOM.createPortal, you could point it to a documentFragment in memory rather than an element on the page. The nodes that get created should then be appended to that fragment in memory. Whether or not this meets your criteria or not i'm not sure, but it would look something like this:
class Example extends Component {
constructor (props) {
super(props);
this.fragment = document.createDocumentFragment();
}
render() {
return ReactDOM.createPortal(
this.children,
this.fragment
);
}
}
2) Create a custom reconciler.
If you want the data to be output in some particular format instead of being DOM nodes, you could create a custom reconciler. This is non-trivial thing to do and uses the react-reconciler package, which the react team describes as "experimental", but it would give you very precise control of what to do with the results of the render.
If this is something you want to do you can read some at the react-reconciler readme, and it may be useful to look at the configuration files that are used for other reconcilers (see the "host config" links near the bottom of the readme)

Resources