How does React provide independent contexts for subtrees? - reactjs

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

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.

Alternative to Reactdom.render and unmountComponentAtNode in react18

Important note:
I am aware of createRoot and root.unmount()! Unfortunately (If I understand this correctly) they should be used just once in the application for mounting the react application.
Problem description:
In our app we have a modal component that is rendered dynamically and added to the body of the html via ReactDOM.render(). When this modal is hidden, we unmountComponentAtNode().
Unfortunately, after upgrading to react18, unmountComponentAtNode becomes deprecated and the new unmount is (in my understanding) for the root only. The same problem is about if I try to modify the ReactDOM.Render() for createRoot. Then we would have 2 roots in the app which is wrong.
What is the proper way to attach the modal to the body element (next to root!) and unmount it after it should be destroyed? The implementation is a little bit "weird" (partially in jsx, partially not...) and I would like to avoid refactoring the whole component as there will be a lot of refactoring already in the code... So I would like to focus on refactoring this component (into jsx one) later. Now I have to figure out only the rendering / unmounting. I have been thinking about using Portals, but anyway I have to create that elements somehow and render them into the DOM where portals does not help me a lot.
Calling the createRoot and then render on the root in this modal component fires an error You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before. Instead, call root.render() on the existing root instead if you want to update it. which is obvious. But there is no "useRoot()" hook or anything like that. Should I store the returned object (root) in some context or somewhere to use it later? Or what should be the best option to call the render? :/
I know how I should do that with classical functional component... But maybe there is some way that I can just refactor a piece of the code instead of the whole component and all its usecases. Maybe there is something I am not aware of (there is definitely thousands of things I am not aware of :D) that should simplify my life...
function modal() {
return (
<div>
...
</div>
)
}
Modal.show = () => {
modalEl = document.createElement('div');
util.destroy(el) => {
ReactDOM.unmountComponentAtNode(el);
el.remove();
}
const childs = props.childs;
REactDOM.render(childs, modalEl);
}
When I was thinking about portals, I thought I will just rewrite the last line of ReactDOM.render to portal like createPortal(childs, modalEl), unfortunately this does not render anything (except modalEl, but no childs inside). The childs are of type ReactNode (using typescript) and they are not empty (because of ReactDOM.render works without any problem).

React.forwardRef is already possible without it, so what's the use of it?

I'm confused on the point of React.forwardRef. As explained in its documentation, I understand that its main use is for a Parent Component to gain access to DOM elements of the Child Component. But I can already do that without even having to use it.
Here is a code example that you can plug into CodeSandbox and see that it works:
import React, {useRef, useEffect} from "react";
import "./styles.css";
const ChildComponent = (props) => {
useEffect( ()=> {
props.callbackFunction()
})
return(
<div ref={props.fRef}>
{"hello"}
</div>
)
}
export default function App() {
const callbackFunction = () => {
console.log("The parent is now holding the forwarded ref to the child div: ")
console.log(forwardedRef)
}
const forwardedRef = useRef(null)
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<ChildComponent name="gravy" callbackFunction={callbackFunction} fRef={forwardedRef}/>
</div>
);
}
Or here's the embed of this example. Honestly, I'm kind of new to this and I don't know exactly how embeds work and whether someone fiddling with the embed changes my original Sandbox or not, so I was hesitant to put it. But here it is.
Example Forwarding Ref
In the example, the parent App() component successfully passes a ref to the child which the child attaches to its rendered div. After it renders, it calls a callback function to the parent. The parent then does a console log where it proves that its forwarded ref now has a hold of the child's div. And this is all done without React.forwardRef.
So what then is the use for React.forwardRef?
You're absolutely right that you can do what you've described. The downside is that you're forced to expose an API (ie: the fRef prop) for it to work. Not a huge deal if you're a solo developer building an app, but it can be more problematic eg. if you're maintaining an open-source library with a public API.
In that case, consumers of the library won't have access to the internals of a component, meaning you'd have to expose it for them somehow. You could simply do what you're suggesting in your example and add a named prop. In fact, that's what libraries did before React 16.3. Not a huge deal, but you'd have to document it so people know how to use it. Ideally, you'd also want some kind of standard that everyone used so it wasn't confusing (many libraries used the innerRef naming convention), but there'd have to be some consensus around that. So all doable, but perhaps not the ideal solution.
Using forwardRef, passing a ref to a component just works as expected. The ref prop is already standardized in React, so you don't need to go look at docs to figure out how to pass the ref down or how it works. However, the approach you describe is totally fine and if it meets your needs, by all means go with that.
As mentioned in the docs , it's useful for highly reusable components, meaning components that tend to be used like regular HTML DOM elements.
This is useful for component libraries where you have lots of "leaf" components. You've probably used one like Material UI.
Example:
Let's say you're maintaining a component library.
You create a <Button/> and <Input/> component that maybe just adds some default styling.
Notice how these components literally are just like regular HTML DOM elements with extra steps.
If these components were made to be used like regular HTML DOM elements, then I expect all the props to be the same, including ref, no?
Wouldn't it be tedious if to get the button ref from your <Button/> component I'd have to get it through something like fRef or buttonRef ?
Same with your <Input/>, do I have to go to the documentation just to find out what ref to use and it's something like inputRef ? Now I have to memorize?
Getting the ref should be as simple as <Button ref={}/>
Problem
As you might know, ref will not get passed through props because, like key, it is handled differently by React.
Solution
React.forwardRef() solves this so I can use <Button ref={}/> or <Input ref={}/>.

What's the actual cost of defining new function within react render method?

I'm trying to clarify the pros/cons of having a new function declaration within react's render method.
Consider a render method like the following:
render () {
return (<SomeComponent somePropWithoutEventBind={() => console.log('not so dangerous?')} />)
}
In the above example, somePropWithoutEventBind doesn't bind to a DOM event: react will check for prop changes and every time render is called this prop has changed - because it's a new function - so it never matches the previous, this is expensive but nothing tremendous.
Now in this case
render () {
return (<input onChange={() => console.log('dangerous?')} />)
}
onChange prop does bind to DOM (doing something like addEventListener) so every render will have to removeEventListener and addEventListener again? Is this the main reason behind avoiding to declare functions inside the render method?
If possible, please justify your answer pointing to react source code.
The main reason of avoiding defining new functions in render is to avoid over rendering.
Consider bind a new function onto a DOM element (react element not real DOM) like so: <button onClick={_ => this.setState({ hide: true })}>Hide Me</button>} there's almost none cost at all, since DOM elements gets re-rendered anyways. (site note: react doesn't use native DOM events like add/removeEventListener, it uses SyntheticEvent and your code targets virtual DOM aka react element not real DOM)
However for a custom components (In large codebase we typically have lots of complex Container Component composed of Functional/Class Child Components. Let's say you have something like
render() {
// you won't run into unnessary re-render issue
// when you use `onClick={this.handleClick}` since the function reference doesn't change
// while most perf tricks done by react bindings are relying on shallow compare of props/state
return (
<ComplexContainer onClick={_ => this.setState({ forceReRender: true})}>
<Child1 />
<Child2>
<NestedChild1 />
<NestedChild2 />
</Child2>
</ComplexContainer>
)
}
If you do this way, this will cause the whole render tree starting from ComplexContainer to re-render, this may have notable negative perf impacts, but you will need DevTools profiling to benchmark.
In fact, the real thing i wanna say is: it might not be that huge as you concern, avoid premature optimization can be more important. Give this awesome reading material a shot: React, Inline Functions, and Performance
A bit more info regarding react synthetic event system here, it's simply a wrapper of native DOM events to normalize the subtle differences of events among different browser vendors. The API would be the same event.preventDefault()/event.preventPropagation() etc works as it is, but you get cross-browser compatibility for free. Regarding how it works internally please see event delegation

How to call method on a JSX element when writing functional react

I have recently started using React and Redux. One thing that often messes with my brain is how to re-write all the code examples from documentations that are usually written object based to my functional code base.
I am now in one of those situations; I can not find a way to call a method belonging to react-custom-scrollbars (link to docs) which I am using in one of my components. Below is a simplified version of the component. I have commented out the section where I would like to call the method scrollToBottom().
Bonus question: If I skip using the onUpdate() event, how would I go proceed if I want to call scrollToBottom() when a message is appended to the messages array?
const Chat = ({messages, app, keyDown, pressSend, setMessage, toggleEnter}) => {
return (
<div id="orbit-chat-content">
<Scrollbars
onUpdate={() => {
//
// HERE I WANT TO SCROLL TO BOTTOM
//
// this.scrollToBottom()
//
}}
className="react-scrollbar">
<div id="orbit-chat-conversation">
{ messages.map(message => <Message {...message} />) }
</div>
</Scrollbars>
</div>
);
};
export default Chat;
Thank you very much for taking your time to look at this!
The answer to your question:
Stateless components don't have refs. Which you would normally use to access the scrollbars instance.
Your real problem:
...how to re-write all the
code examples from documentations that are usually written object
based to my functional code base.
You don't have to. Statefull components are not deprecated or so. They are the base. PureRender Components / Functional components, are just an addition to the stack to provide a way of writing small independent components, like a Button.
Of course you can write a whole app only with stateless components, but if you need internal state, access to instances, some internal logic, you can and should use Normal Components too.

Resources