EDITED because my original question was not worded right.
I'm trying to understand why ReactDOM is being used to render some HTML divs, and other rendering is done in React. Is there something I'm missing here?
An example from a React course I took recently, this is the code in one of the exercises that uses react-dom:
import React, {Component} from 'react'
import {render} from 'react-dom'
var Bookstore = [
{"title":"Mr. Bean", "author":"Rowan Atkinson", "pages":200},
{"title":"The only Bean", "author":"Chris Dowd", "pages":100},
{"title":"IT crowd", "author":"Rich Ayando", "pages":50}
]
const Book = ({title, author, pages}) => {
return (
<section>
<h1>{title}</h1>
<h3>By {author}</h3>
<p>{pages} pages</p>
</section>
)
}
const Library = ({bookstore}) => {
return (
<div>
{bookstore.map(
(book, i) => <Book key={i} title={book.title} author={book.author} pages={book.pages}/>
)}
</div>
)
}
render(
<div>
<Library bookstore={Bookstore} />
</div>,
document.getElementById('root')
)
While another React file renders this way:
import React, {Component} from 'react'
export const Book = ({title="No title", author="No author", pages=0, freeBookmark}) => {
return (
<section>
<h1>{title}</h1>
<h3>By {author}</h3>
<p>{pages} pages</p>
</section>
)
}
What happens in the real DOM ?
Each time something in the DOM changes. Since DOM is represented as a
tree structure, changes to the DOM is pretty quick but the changed
element, and it’s children’s has to go through Reflow/Layout stage and
then the changes have to be Re-painted which are slow. Therefore more
the items to reflow/repaint, slower your app becomes.
To overcome this react uses virtual DOM
How virtual DOM helps?
it tries to minimize these two stages to get better performance.
virtual means a representation of a UI is kept in memory and synced with the "real" DOM by a library such as ReactDOM.
Difference between render in the component & reactDOM.render ?
Render in the component is used to construct the virtual DOM.
reactDOM.render is used to attach the virtual DOM tree to the real DOM tree after the diffing algorithm detects the changes.
Related
I just watched a talk from React Conf 2018. In the video, the speaker shows 2 ways to set the document title. The first is by using the Lifecycle methods (componentDidMount and componentDidUpdate) if we use a class component, the second one is by using the useEffect hook if we use a function component. It looks like that's the recommended way to do it according to answers from this question.
But, I tested the following code and it seems to work just fine to set the document title directly:
import React from 'react';
import ReactDOM from 'react-dom';
function App() {
document.title = 'wow'
return <p>Hello</p>
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
The title changed:
Is there any significance of setting the document title inside useEffect or componentDidMount?
Or is it because it was the only way to set the document title?
Is it okay to set the document title directly like I did in the snippet above?
Update:
It also works with class component:
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
render() {
document.title = 'wow'
return <p>Hello</p>
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
The thing is, the render function in React, is considered to be a pure function.
A pure function should not have any side-effects. Eventually updating the document.title means that you are referencing the document directly from render-> which is already considered a side effect.
Render should mainly do one thing -> render some JSX, if there is a need to do some API calls/interact with the document/window etc..., this should be placed in componentDidMount/componentDidUpate for a class Component, OR in a useEffect/useLayoutEfect for a functional component based on hooks.
(useEffect for e.g. is a function that runs asynchronously, and updating the document title from it will not block the rendering, whereas doing it directly will block it.)
Use react-helment which is also widely used in other frameworks with the same name as helmet express ...
Here is the same code:
import {Helmet} from "react-helmet";
class Application extends React.Component {
render () {
return (
<div className="application">
<Helmet>
<meta charSet="utf-8" />
<title>My Title</title>
<link rel="canonical" href="http://example.org/example" />
</Helmet>
...
</div>
);
}
};
Another way to use via props to which is cleaner IMO
...
<Helmet titleTemplate={`%s | ${props.title}`} defaultTitle={constants.defaultTitle} />
You may also use react-helmet-async for more features.
I'm attempting to import a React functionComponent from an SVG and then send that to another component as a prop to render that svg. With the setup below, this compiles fine, but eventually crashes when trying to render the svg in browser with:
Error: Objects are not valid as a React child (found: object with keys {$$typeof, render}). If you meant to render a collection of children, use an array instead.
Classes below are simplified. But the gist of what I'm trying to do is:
In overlay.tsx:
import { ReactComponent as icon } from "/icon.svg";
import CustomItem from "/customItem";
const Overlay: React.FC<OverlayProps> = () => {
return (
<div>
<CustomItem icon={icon}/>
</div>
);
export default Overlay;
}
and in customItem.tsx:
import React from "react";
export interface CustomItemProps {
icon: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
}
const CustomItem: React.FC<CustomItemProps> = ({icon}) => {
return (
<div>
{icon}
</div>
);
};
export default ApplicationsDropdownItem;
I assume my problem is somewhere around the syntax of {icon}, but I can not for the life of me find out what I'm suppose to use instead.
Answer
The icon you are importing is a component, therefore it must be called to render the JSX.
<Icon {...props}/> (correct) or {Icon(props)} (not recomended)
Since it is a component, you should also name it Icon and not icon.
Take a look at this blog post that explains SVGR.
TL;DR - Best approach for rendering components
A. Call the component in your render method with component syntax <MyComponent/> not MyComponent().
B. Instantiate your component as a variable, and pass that to your render method's JSX block.
More info
#DustInCompetent brought to light the issue of calling a component as a function inside a JSX block.
As explained here and here, that will lead to react not registering a components hooks and lead to state and other problems.
If you are implementing a High Level Component (HOC), then you should not call a component within the render method (return statement in functional components), as this leads to problems for similar registration issues of the component.
import React from "react";
import { ReactComponent as SampleIcon } from "/sample_icon.svg";
export interface CustomItemProps {
Icon: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
}
const CustomItem: React.FC<CustomItemProps> = (props) => {
const Temp = props.Icon as React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
return (
<div>
<Temp/>
</div>
);
};
<CustomItem Icon={SampleIcon}/>
I think you should use <Icon /> instead of {icon} because it's a component.
I'm very new to react and I just want to use this https://github.com/jackocnr/intl-tel-input plugin in my form. But in the documentation says to use pure js input id query selector to use plugin. But how can I change this kind of code to react usage.
code example in doc:
<input type="tel" id="phone">
<script src="path/to/intlTelInput.js"></script>
<script>
var input = document.querySelector("#phone");
window.intlTelInput(input);
</script>
You should avoid to use Jquery in React projects. You can do it React way.
For intl-tel-input, there is react-intl-tel-input and react-intl-tel-input-v2 packages are available.
import ReactIntlTelInput from 'react-intl-tel-input-v2';
import 'intl-tel-input/build/css/intlTelInput.css';
<ReactIntlTelInput
value={this.state.value}
onChange={this.onChange}
/>
Demo
It's not a good idea to manipulate the DOM manually since react creates a virtual DOM for performance issues
React creates a tree of custom objects representing a part of the DOM.
For example, instead of creating an actual DIV element containing a UL
element, it creates a React.div object that contains a React.ul
object. It can manipulate these objects very quickly without actually
touching the real DOM or going through the DOM API. Then, when it
renders a component, it uses this virtual DOM to figure out what it
needs to do with the real DOM to get the two trees to match.
You can think of the virtual DOM like a blueprint. It contains all the
details needed to construct the DOM, but because it doesn't require
all the heavyweight parts that go into a real DOM, it can be created
and changed much more easily.
source
There's a lot of "ready-made" react components that you can use. You can use react-phone-number-input for example like this:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "react-phone-number-input/style.css";
import PhoneInput from "react-phone-number-input";
import "./styles.css";
function App() {
const [inputValue, setInputValue] = useState("");
return (
<div className="App">
<h1>International phone number</h1>
<PhoneInput
placeholder="Enter phone number"
value={inputValue}
onChange={inputValue => setInputValue(inputValue)}
/>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Take a look at codesandbox
If you want to learn more about how React manipulates the DOM, you can read this article.
I've created a simple app to test what part of the document gets rerendered when I add items to an array and then use .map in react. To manage the state I use redux. To check what gets rerendered I use the react chrome addon with the option Paint flashing selected.
So I expect that when I dispatch an action from a component that modifies the store, only the components connected to that part of the store would flash green. Instead, the whole screen flashes green followed by every single component that will also flash green.
Seems like anything under <Provider /> will just update on any change inside redux store.
I've already tried PureComponent, managing shouldComponentUpdate, React.memo for the function component.
My index file looks like:
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore } from "redux";
import reducers from "./store/reducers";
import "./index.css";
import App from "./App";
const store = createStore(reducers);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
And my App file:
import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";
import ListComp from "./components/ListComp";
import ListFunc from "./components/ListFunc";
import ButtonMore from "./components/ButtonMore";
export class App extends Component {
shouldComponentUpdate() {
return false;
}
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
<ButtonMore />
<ListComp />
<ListFunc />
</div>
);
}
}
export default App;
ButtonMore will add items to the store when clicked. It has the action connected so it can dispatch it.
ListComp is connected to the list of items in the store and will .map them. In this case, the main purpose was to test the key property and see if only the new items would flash green.
ListFunc Will do the same as the one above but as a function component.
I wanted to drive this test since in the project I work on we are going crazy with performance issues now that the app is huge. We are thinking of moving away from redux but I don't think this option is good at all.
I expected some green flashes just on the new items displayed. But instead the whole screen will always flash when I change anything in the store.
EDIT: Let me add the example that shows the list of items from the store. I expected this to flash only the new items but instead it flashes the whole component:
import React from "react";
import { connect } from "react-redux";
const ListFunc = props => {
return (
<ul className="ListComp">
{props.listItems.map((item, i) => {
return <li key={`itemFunc_${i}`}>{item}</li>;
})}
</ul>
);
};
const mapStateToProps = state => {
return { listItems: state.reducer };
};
export default connect(
mapStateToProps,
null
)(ListFunc);
React-Redux v6 changed the internal implementation in several ways. As part of that, the connect() wrapper components do actually re-render when an action is dispatched, even when your components don't.
For a variety of reasons, we're changing that behavior as part of v7, which is now available as a beta.
update
After looking at the code snippet you've posted: yes, I would still expect the example you've shown to cause both the list items and the list to re-render. I can't say 100% for sure because you haven't shown your reducer, but assuming that one of the list items is updated properly, state.reducer should be a new array reference as well. That will cause ListFunc to re-render.
I quite new to react. What is ref={node => (this.root = node)} for in:
import React from 'react';
import Navigation from './Navigation';
import Link from '../Link';
import s from './Header.css';
class Header extends React.Component {
componentDidMount() {
window.componentHandler.upgradeElement(this.root);
}
componentWillUnmount() {
window.componentHandler.downgradeElements(this.root);
}
render() {
return (
<header className={`mdl-layout__header ${s.header}`} ref={node => (this.root = node)}>
<div className={`mdl-layout__header-row ${s.row}`}>
<Link className={`mdl-layout-title ${s.title}`} to="/">
Igene Logo Here
</Link>
<div className="mdl-layout-spacer"></div>
<Navigation />
</div>
</header>
);
}
}
export default Header;
( from react-static-boilerplate (RSB) )
and it's relationship to mount and unmounting?
I get the concept of ref based on reading this, but still find it quite hard to understand when it is used in RSB. Can someone enlighten me how it works and also it's relationship to the componentDidMount() and componentWillMount()
The following:
ref={node => (this.root = node)}
stores a reference to the DOM element that it is contained within. Your example sees it inside a header tag, which makes it possible to then manipulate the actual DOM element of the header by referencing this.root.
One of the main use cases for storing refs is that the element becomes available to other members of the same component class.
Examples include:
focusing on a particular form input within componentDidMount
or disabling input into a text field after another one has been filled (these are examples are quite rough, but hopefully you get the idea).