ReactJS: ReactDOM.Render() in a Variable? - javascript

I can't really find any documentation for this but is it possible to put ReactDOM.Render() in its own render/function variable?
The reason for this is I want to stream line my codebase and when trying to render multiple components with props etc it gets a little messy.
Example:
ReactDOM.render(
<SuccessErrorModal status={props.status} progress={50} />,
document.getElementById("loading-modal")
);
Desired Result:
const LoadingModal = (props) => {
ReactDOM.render(
<SuccessErrorModal status={props.status} progress={50} />,
document.getElementById("loading-modal")
);
}
<LoadingModal status={true} /> // Calling the ReactDOM.Render()
Create Portal Example (Not working):
const LoadingModal2 = () => {
ReactDOM.createPortal(
<SuccessErrorModal status={true} progress={50} />,
document.getElementById("loading-modal")
);
};
TIA

You can use ReactDOM.createPortal for this.
Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.
You can get more information here

Related

user-defined component in React

Why does 'element' define what looks like an HTML tag that call's a function.
Why would i not set element to something like: Welcome('Sara')
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
The format resembling HTML is called JSX:
it is a syntax extension to JavaScript. We recommend using it with
React to describe what the UI should look like
Technically (in this simple case) you could replace the JSX with Welcome({ name: 'Sara' }), but that is not the standard way to render components in React - and it has some technical implications because it is not the same as using JSX (which calls React.createElement).

React: How to use refs in a function component that I have no control over (i.e. from a library)

I can't believe I couldn't find an answer on this so please point me in the right direction if I am wrong.
I am trying to do something simple:
There's a function component from a library that I include.
I need to get a ref to it.
Crucially, React says this:
Ref forwarding lets components opt into exposing any child component’s ref as their own.
Unfortunately, this library component has not opted in using forwardRef.
What is the correct approach to get a ref to this element?
The 3rd party library component in question looks like so (simplified):
function OutboundLink(props) {
return (
<a {...props}
...
/>
)
}
My component looks like so:
function MyComp(props) {
const ref = useRef(null)
return (
<OutboundLink ref={ref} {...props} >
{children}
</OutboundLink>
)
}
but I am greeted with the errors about not being able to pass refs to a function component. Normally I would wrap in forwardRef, but can't in this case since I can't modify the library's code directly.
I did see this and this but didn't think either applied to me.
Any ideas?
You can't directly. There's no hook to let you in if the component wasn't written that way.
I think your best bet would be to wrap the component, capture a ref, and then drill into its children with DOM methods.
const wrapperRef = useRef(null)
const innerRef = useRef(null)
useEffect(() => {
if (wrapperRef.current) {
innerRef.current = wrapperRef.current.querySelector('a')
// innerRef.current is now the first <a> tag inside <OutboundLink>
}
})
return <div>
<div ref={wrapperRef}>
<OutboundLink {...props}>
{children}
</OutboundLink>
</div>
</div>
Codepen example (view the console)
But really, this is a bit of a hack. You should use a component that handles this better. Look into how hard it would be to write this component yourself from scratch. It may be trivial, and worth it.

how does the .render() function in react work internally?

I can define a component like this:
class Welcome extends React.Component {
render() {
return <h1>Hello!</h1>;
}
}
When I want to render this object in the Dom (or rather add it to the virtual DOM) I call
ReactDOM.render(
<Welcome />,
document.getElementById('root')
);
That means that at some point multiple .render() functions are nested inside of each other since we defined return <h1>Hello!</h1>; inside of the .render() function of Welcome.
It also means that I can use the .render() method to render an Object to the DOM and to define a new react object.
This syntax is from the official documentation
Does .render() just render things to the virtual DOM and the nesting is resolved by React internally or is there more behind it?
From Rendering a Component:
(updated to reflect your code)
class Welcome extends React.Component {
render() {
return <h1>Hello!</h1>;
}
}
ReactDOM.render(
<Welcome />,
document.getElementById('root')
);
We call ReactDOM.render() with the <Welcome /> element.
React calls the Welcome component with {} as the props.
Our Welcome component returns a <h1>Hello!</h1> element as the result.
React DOM efficiently updates the DOM to match <h1>Hello</h1>.
It may be beneficial to read up on the React Life Cycle.
To understand render you have to understand how react works internally. To understand this in greater detail I would recoment reading this but here is a quick overview:
Conceptually a react component is a just a function that returns an Object like this:
{
type: 'button',
props: {...}
};
So the render() part of a class component just specifies what part of the component will be returned. The react virtual dom is made up of many of these object nested inside of each other (in props.children). React will start at the top object, turn it into an html node and render it to the dom, then do the same for all of its children etc.

In ReactJs what is the difference between a component and an element?

I see the 2 terms being used interchangeably a alot.
This has caused me a great deal of confusing, could someone please explain to me the differences?
A component is a set of Elements (as the documentation says):
Note: One might confuse elements with a more widely known concept of
“components”. We will introduce components in the next section.
Elements are what components are “made of”, and we encourage you to
read this section before jumping ahead.
Here I could tell you two quick differences:
The Elements do not have Props or states, they are simply something visual (but you can execute Javascript using {}.
At the time of rendering, the elements are used as any variable, unlike a component that is through the labels, for example:
// This is a component
ReactDOM.render(<Element />, document.getElementById('root'));
// This is an Element
ReactDOM.render(element, document.getElementById('root'));
An element is something assigned to a JSX value:
const foo = <p>My name is foo</p>;
We can render elements into DOM with:
ReactDOM.render(foo, document.getElementById('root'));
You can create a whole app by combining different elements.
const name = "foo";
const greeting = (
<div>
<h1>Hello, {name}</h1>
<p>Welcome!</p>
</div>
);
ReactDOM.render(greeting, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Of course without flexibility and some other advantages like state and life cycle methods it will be sure very hard to write a whole app. So, this is why we have components.
A component is a function which returns JSX element or element compositions.
const Greeting = ( props ) => (
<div>
<h1>Hello, {props.name}</h1>
<p>Welcome!</p>
</div>
);
ReactDOM.render(
<Greeting name={"foo"} />,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
So, components actually are functions optionally taking props and return elements. The above code is an example for "functional components". To use state and life cycle methods we have to use "class components".
class Greeting extends React.Component {
state = {
name: "",
}
getName = () => new Promise( resolve =>
setTimeout( () => resolve( "foo" ), 1000 )
);
componentDidMount() {
this.getName().then( name => this.setState( { name } ) );
}
render() {
const { name } = this.state;
return (
<div>
<h1>Hello,
{
!name
? <span> stranger.</span>
: <span> {name}.</span>
}</h1>
<p>Welcome!</p>
</div>
);
}
}
ReactDOM.render(
<Greeting />,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Above component waits for one second, then grab the name value and set it to state. Until it finishes our view says "stranger" instead of the grabbed name. This can be possible thanks to life cycle methods and state.
For React, both of those components are the same. They somehow return JSX in the end of the day. Classes are special functions in Javascript, so this is why I said all of the components are "functions". The difference for React, class based components have states and life cycle methods.
Of course components can return other components as well but main difference with a component and element is one of them is just a JSX value, the other one is a function returns elements.
The big difference is, apart from being a function and returning some other elements, of course we have state and life cycle methods for components. If an element is a brick then a component is something like a wall.

Grouping a large set of ReactDOM.render() calls in a root component

Let's say I have a large HTML document with numerous React Components embedded in parts of it.
For example:
<body>
<h1 id="react-component-1">...</h1>
<p>Static Content</p>
<h2> id="react-component-2">...</h2>
<img src="static-img" />
<p id="react-component-3">...</h3>
</body>
As you can see, there are static elements in-between my dynamic elements. I'd imagine that this situation is common in web sites that are slowly bringing React in to there toolchain.
Right now my application works like this:
function renderPage(data) {
ReactDOM.render(<Component {...data} />, document.getElementById("react-component-1"));
ReactDOM.render(<Component {...data} />, document.getElementById("react-component-2"));
ReactDOM.render(<Component {...data} />, document.getElementById("react-component-3"));
}
renderPage() is called upon the success of an ajax request. This makes my components update as intended, and it seems pretty performant.
However, it would be much nicer if I had a root React component that handled the state of the application, and pushed down changes to data as props.
I can't make a root component though because not everything on my page is dynamic, I need to preserve the static content in-between the React components.
I would love to be able to do something like this though:
class Root extends React.Component {
constructor(props) {
super(props);
this.data = {...};
}
render() {
ReactDOM.render(<Component {...this.state.data} />, document.getElementById("react-component-1"));
ReactDOM.render(<Component {...this.state.data} />, document.getElementById("react-component-2"));
ReactDOM.render(<Component {...this.state.data} />, document.getElementById("react-component-3"));
}
return false;
}
Is it possible to use React this way? At first I unsure of how it would be possible, because ReactDOM expects an element to render components in to, which would clear out my static content when I render my root component. Now I am thinking I could just do new Root(), but I have yet to test this.
Another option that I just thought of was keeping the renderPage() function and calling it ONCE instead of every time AJAX is loaded. Each component would then listen for a custom event that is fired on the AJAX success, which contains the new data. This seems slower though... And I might as well use Redux for the application state.
You shouldn't use the render method of a React component to call ReactDOM.render the way you do it in your example Root component. The render method of a component can return either a valid React element (for example, <div />) or null and is not supposed to do any mutations, such as modifying the DOM, etc. If a React component needs to do some DOM mutations, it should do it from lifecycle hooks such as ComponentDidMount or ComponentDidUpdate.
To answer you question, it depends whether your static content, can be included in a React component or not.
If you can, which would be ideal, you can refactor your code to have a Root component like so :
class Root extends React.Component {
render() {
return (
<Component data={ this.props.data } />
<p>Static Content</p>
<Component data={ this.props.data } />
<img src="static-img" />
<Component data={ this.props.data } />
);
}
}
and to have one call to ReactDOM.render :
ReactDOM.render(<Root data={ data } />, document.getElementById('my-react-mounting-point'));
If you cannot, that is if you need to have different React render trees, your current approach is fine. If you do this, though, be careful to call ReactDOM.unmountComponentAtNode when you do not need the component anymore, or you might face memory leak issues. This article from React's blog has several nice tips about the precautions to use when having various render trees + a ReactComponentRenderer boilerplate to avoid common pitfalls.

Categories