Calling ReactDOM.render more than once - reactjs

Why does the following only render a single button?
const b = <button>this is a button</button>;
ReactDOM.render(b,mountNode)
ReactDOM.render(b,mountNode)
ReactDOM.render(b,mountNode)

If mountNode is a reference to a DOM element, calling ReactDOM.render(b, mountNode) means that React will insert your React Component as the innerHTML to that node.
Calling it several times effectively means that you just keep replacing the previously mounted node.
If you want 3 buttons, try creating a component that wraps them. For example:
var mountNode = document.getElementById("app");
const b = <button>this is a button</button>;
const myApp = <div>{b}{b}{b}</div>;
ReactDOM.render(myApp, mountNode);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<div id="app"></div>
Alternatively:
var mountNode = document.getElementById("app");
const B = () => <button>this is a button</button>;
const MyApp = () => <div><B /><B /><B /></div>;
ReactDOM.render(<MyApp />, mountNode);
<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="app"></div>

In react it creates virtual DOM. Every time render method is called the previous DOM is replaced by new created DOM. It only looks for difference between previous DOM and new DOM. That's why it renders single button.

Related

Get Root DOM element from a child component

I have a loop that reads multiple elements from the document and I render it with ReactDOM.render, and a component very low in the component tree, creates a custom event, to that event I would like to pass the element that was rendered in the DOM (i.e. Root Element), I have to go passing from the top the element through Props, or React provides some API that can tell me which Root element we are?
Rather, in the child component, I would like to make: rootElement.dispatchEvent(myCustomEvent);
What options do I have to do this?
The root node looks to be given a property that starts with __reactContainer, so you can search through parent elements until you find an element with such a property.
const Child = () => <div><span onClick={(e) => {
let element = e.target;
while (element) {
element = element.parentElement;
if (Object.keys(element).some(key => key.includes('reactContainer'))) {
console.log('Found', element);
break;
}
}
}}>click</span></div>;
const App = () => {
return <section><Child /></section>
};
ReactDOM.createRoot(document.querySelector('.react')).render(<App />);
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div class='react'></div>
That's almost certainly not part of the deliberate outward-facing design, though. A better way would be to use useContext to save the root element at the top component via a ref, and to consume it in the descendant component.
const Child = () => {
const { ref } = React.useContext(Context);
return (
<div>
<span
onClick={() => { console.log(ref.current.parentElement); }}
>click</span>
</div>
);
};
const Context = React.createContext();
const App = () => {
const ref = React.useRef();
return (
<Context.Provider value={{ref}}>
<section ref={ref}><Child /></section>
</Context.Provider>
);
};
ReactDOM.createRoot(document.querySelector('.react')).render(<App />);
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div class='react'></div>

How to implement a React equivalent of Vue's IS attribute?

I want to be able to implement:
<div is='a' href='https://stackoverflow.com'>go to Stack Overflow</div>
To render into:
<a href='https://stackoverflow.com'>go to Stack Overflow</a>
What would be the most sensible approach to implement it?
There are a number of ways to do that, but here's one of them:
const CustomComponent = (props) => {
const {is, children, ...rest} = props
return React.createElement(is, {...rest}, children)
}
const App = () => {
return (
<CustomComponent
is={'a'}
href={'https://stackoverflow.com'}
>
go to Stack Overflow
</CustomComponent>
)
}
ReactDOM.render(
<App /> ,
document.getElementById('root')
);
<script src="https://unpkg.com/react#17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>
More on React.createElement() here.

using state in dangerouslySetInnerHTML react component

I have a string about html builded by react.
I'm trying to implement render this html string through react, and looking for solution to manage state.
below is simple example.
const App = (props) => {
let code = '<b>Will This Work?</b>';
return (
<div dangerouslySetInnerHTML={ {__html: code} }>
</div>
);
}
I want to manage the state of component rendered with dangerouslySetInnerHTML option.
Can i get any ideas about how approach?
What is dangerouslySetInnerHTML?
It is a way to set the children of the component (as text/html).
Can state be used in it?
Yes state can be used with it; However, the innerHTML MUST be vanilla HTML, NOT JSX. An example of this can be seen below:
(click the button to change the state from text to a red div)
The way this is working is because the state can be inserted into a string literal which will then be handled as HTML encoded text.
const App = (props) => {
const [state,setState] = React.useState("something I set with state");
let code = `<b>Will This Work? ${state}</b>`;
return (
<React.Fragment>
<button onClick={()=>{setState("<div style=\"background-color: red; width:50px; height:50px;\"><div>")}}>Click to change state from pure text to an html element</button>
<div dangerouslySetInnerHTML={ {__html: code} }>
</div>
</React.Fragment>
);
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.querySelector('.react')
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>
Should you do this?
Probably not. There is a reason it is called dangerouslySetHTML and not safelySetInnerHTML.
See more in the React Docs

Basics implementation example of useState Hooks in ReactJS

We know that the useState hook is used for managing state within functional components in ReactJs.
So, for learning purposes I was implementing the example of the useState hook (snippet is given below) wherein I have taken an array with some initial value and I need to update the array and display the whole updated array in the browser whenever I clicked on the button. I tried with the below snippet but didn't get the expected result.
Problem: When I click the button for first time it will add the new element in the array but after clicking the button for two or more times it only overrides the last element.
Expected Result: New elements should be added in the array rather than overriding the last element in the array.
I definitely missing any logic or any important concept of useState hook here in this example. Please help me to understand more on react Hooks.
const {useState} = React;
const Example = () => {
const array = [1,2,3] ;
const [newArray,setNewArray] = useState(array);
const [newElement,setElement]= useState(array[array.length-1]);
const handleBoth = () => {
setElement(prev => prev + 1);
setNewArray([...array,newElement]);
}
const mapping = newArray.map(element => <li> No. {element}</li>)
return (
<div>
<ul>
{mapping}
</ul>
<button onClick={handleBoth}>Add</button>
</div>
);
};
ReactDOM.render(
<Example />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Use the state newArray not the array for the map implementation. array will reinitialize to [1,2,3] on every render. In fact, you should just move the array constant outside of the component
const {useState} = React;
const array = [1,2,3];
const Example = () => {
const [newArray,setNewArray] = useState(array);
const [newElement,setElement]= useState(array[array.length-1]+1);
const handleBoth = () => {
setElement(prev => prev + 1);
setNewArray([...newArray,newElement]);
}
const mapping = newArray.map(element => <li> No. {element}</li>)
return (
<div>
<ul>
{mapping}
</ul>
<button onClick={handleBoth}>Add</button>
</div>
);
};
ReactDOM.render(
<Example />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

React: How to mount all components on page?

I want to be able to mount React components based on the HTML markup of a document rendered by a PHP framework and I can't find a way to achieve this.
Here is the HTML markup of my document:
<html>
<head>
<link rel="stylesheet" href="css/index.css">
</head>
<body>
<div id="app">
<Foo bar="lorem"></Foo>
<Foo bar="ipsum"></Foo>
<Foo bar="dolor"></Foo>
</div>
<script src="js/index.js"></script>
</body>
</html>
Here is the Foo component implementation (foo.js):
const React = require('react');
class Foo extends React.Component {
render() {
return React.createElement('div', {
className: 'foo'
}, `Hello ${this.props.bar}`);
}
}
module.exports = Foo;
And here is my App implementation (app.js):
const React = require('react');
const ReactDOM = require('react-dom');
require('./foo');
ReactDOM.render(
React.createElement('div'),
document.getElementById('app')
);
module.exports = {};
I'm expecting React to recursively mount every components found inside the #app div but for some reason only the #app div is mounted resulting in the following markup:
<div id="app">
<div data-reactroot=""></div>
</div>
I can't put my fingers on what I am doing wrong here.
So, in you app.js when you are rendering react DOM
ReactDOM.render(
React.createElement('div'),
document.getElementById('app')
);
In this case you are creating a div element and rendering it inside root div with id=app
In order to create multiple foo elements all you have to do is this
const Foo = require('./foo');
ReactDOM.render(
<div>
<Foo bar="lorem"></Foo>
<Foo bar="ipsum"></Foo>
<Foo bar="dolor"></Foo>
</div>
document.getElementById('app')
);
Hope this helps

Resources