'clientHeight' not counting all element - React - html

The DOM-tree rendered was
<Grid fluid ={true}>
<Row>
<NavigationBar
buttonClass='nav-button'
className='nav-bar'
/>
</Row>
<section id={sectionList[0]}>
<Row className='logo-row'>
<Col className='logo-wrap' xs={3} sm={3} md={2}>
{Logo}
</Col>
<Col xs={9} sm={9} md={10}>
{ProgrammerName}
</Col>
</Row>
{backgroundImg}
</section>
</Grid>
And I was trying to check the clientHeight of the <section> by using following method:
const height = document.getElementById(sectionList[0]).clientHeight;
However, it seems that this call would only give out the height of the <Row> contained in the <section>, ignoring the {backgroundImg} expression, which itself called to render another <Row> component.
<Row>
<FullRowImage src={this.props.src} alt={this.props.alt} />
<h2>
<span>
Some Text
</span>
</h2>
</Row>
What might be the cause of this issue, that clientHeight counts only part of the <section> while leaving out the other?
Thanks.

So I finally figured this out.
As <FullRowImage /> renders an <img> itself, the clientHeight is called before the <img> is loaded and this would leads to a zero height of <img> as well as <FullRowImage>.
In this case, method componentDidMount() would not be enough since a mounted component does not guarantee a loaded image.
On the other hand, onLoad event will come in handy:
class FullRowImage extends React.Component {
constructor(props){
super(props);
this.state = {
loaded: false,
};
this.handleLoaded = this.handleLoaded.bind(this);
}
handleLoaded(){
console.log(document.getElementById('test').offsetHeight);
this.setState(
{loaded: true,}
);
}
render(){
return(
<img id='test'
onLoad={this.handleLoaded}
src={this.props.src}
alt= {this.props.alt} />
);
}
}
And this will print the height of <img> after it is loaded.
Thanks to this article Detect when images have finished loading with React

Related

destructuring children Prop in react

I have two components one is board component other is square component
when I am passing the props from box to square something strange is happening
Code for Board component is
import React from "react";
import Square from "./Square";
const Board = () => {
return (
<div className="board">
<div className="board-row">
<Square value={1}>
<p>hello</p>
</Square>
<Square value={2} />
<Square value={3} />
</div>
<div className="board-row">
<Square value={4} />
<Square value={5} />
<Square value={6} />
</div>
<div className="board-row">
<Square value={7} />
<Square value={8} />
<Square value={9} />
</div>
</div>
);
};
export default Board;
Code for Square component is
import React from "react";
function Square({ value, children }) {
// the curly braces are just destructring
//the values from props passed
console.log(children);
return (
<button type="button" className="square">
{children}
</button>
);
}
export default Square;
The question is console.log(children) is printing an object on my console whereas {console} is showing normal text on button
why is this happening ??
The thing to remember about JSX is that it is much closer to JavaScript than HTML, despite looking more like the latter. The React Docs include a section which shows how simple tags are equivalent to React.createElement calls, which produce objects describing how to render HTML.
props.children is an object which describes createElement calls happening further up the component tree. This allows it to be passed to further utilities like React.Children or React.cloneElement before finally being passed to the renderer. What you see in the console and what you see in the browser are its states before and after it has finally been rendered.
I assume that you mean that console.log(children) produces something similar to this. And you're not sure why {children} in the return value would produce 'normal' text in your browser window.
The reason for this is that children in the Square component is a Javascript object created by React. Your console doesn't know how to display this any better than showing a textual representation of the properties (as you see in the image).
But when you return {children} in your component function, it is handled by React when rendered to your browser window. React does, in fact, know how to display it 'correctly', hence the button with the text as expected.

The component sc-global has been created dynamically warning

using styled components' createGlobalStyle as a workaround an issue i've had in my project, whereby I only want the background-color of .application to be white when the following component is rendered. The below implementation gives the perfect outcome however I get a warning in the console saying:
"The component sc-global-hEInDv has been created dynamically.
You may see this warning because you've called styled inside another component.
To resolve this only create new StyledComponents outside of any render method and function component."
Here is the code:
const GlobalStyle = createGlobalStyle`
.application {
background-color: white;
}
`;
return (
<div className={classNames("caseview", className)}>
<GlobalStyle />
<Helmet>
<title>{caseName}</title>
</Helmet>
{caseview.panelLinks.hasItems && (
<CaseViewDetailPanels caseview={caseview} />
)}
<Row>
<Column size={hasTaskGroup ? 9 : 12}>
{caseview.introtext && <FormattedText text={caseview.introtext} />}
</Column>
{hasTaskGroup && (
<Column
as={TaskGroupPanels}
size={3}
taskGroupPanels={caseview.taskGroupCollection}
/>
)}
</Row>
<FormRoute model={caseview} />
</div>
);
Can anyone suggest a workaround to suppress this warning?
You are creating the styled-component inside your functional component. If you move it outside the component, this warning will go away.
Move the below styled component, outside.
const GlobalStyle = createGlobalStyle`
.application {
background-color: white;
}
`;

What are the advantages or disadvantages of putting our items in an empty div in React?

When writing classic html we usually put our elements in a div and give this div a class name or id.
But in react, we place our elements in an empty div. We do not give both id and class name to this div.
What is the main reason for this? Can you explain?
My code sample is below.
Empty div code example
`
import classes from '../Input/Input.module.css';
const LoginPage = () => {
return (
<div>
<div className={classes['login-page']} >
<form>
<h1 className={classes['login-header']} >Login</h1>
<div className={classes.block}>
<label>Email:</label>
<input type="email"
className={classes['classes-inputs']} />
</div>
<div className={classes.block}>
<label>Password:</label>
<input type="password"
className={classes['classes-inputs']}
/>
</div>
<div className={classes.block}>
<input type="submit" value="Login" />
</div>
</form>
</div>
</div>
)
}
export default LoginPage;
`
Non-empty div code sample
`
import classes from '../Input/Input.module.css';
const LoginPage = () => {
return (
<div className={classes['login-page']} >
<form>
<h1 className={classes['login-header']} >Login</h1>
<div className={classes.block}>
<label>Email:</label>
<input type="email"
className={classes['classes-inputs']} />
</div>
<div className={classes.block}>
<label>Password:</label>
<input type="password"
className={classes['classes-inputs']}
/>
</div>
<div className={classes.block}>
<input type="submit" value="Login" />
</div>
</form>
</div>
)
}
export default LoginPage;
`
You don't have to actually.
The only limitation is that you must return a single element, so in the case you don't have a wrapper already you put an empty div around.
If you don't want to have an empty div you can use fragments <></> to wrap your elements
In most cases the wrapper div is “irrelevant” and is only added because React components require you to return only one element. This kind of behaviour results in useless markup and sometimes even invalid HTML to be rendered, which is bad.
For example we could have a component Table that renders an HTML table and inside that table the columns are rendered with another component called Columns. It would probably look something like this.
`
class Table extends React.Component {
render() {
return (
<table>
<tr>
<Columns />
</tr>
</table>
);
}
}
class Columns extends React.Component {
render() {
return (
<div>
<td>Hello</td>
<td>World</td>
</div>
);
}
}
`
This would result in an invalid HTML to be rendered because the wrapper div from Columns component is rendered inside the .
`
<table>
<tr>
<div>
<td>Hello</td>
<td>World</td>
</div>
</tr>
</table>
`
React fragments let you group a list of children without adding extra nodes to the DOM because fragments are not rendered to the DOM. So basically we use React.Fragment where we would normally use a wrapper div.
We can make use of fragments with <React.Fragments> syntax. So we could write the Columns component as follows.
`
class Columns extends React.Component {
render() {
return (
<React.Fragment>
<td>Hello</td>
<td>World</td>
</React.Fragment>
);
}
}
`
The problem here is JSX parser I think.
JSX and React can return only one component so we can not do something like below
return (
<ComponentA />
<ComponentB />
<ComponentC />
)
You must put a outermost wrapper for all of them and most of the time we use <div> tag
return (
<div>
<ComponentA />
<ComponentB />
<ComponentC />
</div>
)
But you can use React.Fragment or its shortcut <> for opening tag and </> for closing tag.
return (
<> // Can replace with <React.Fragment>
<ComponentA />
<ComponentB />
<ComponentC />
</> // Can replace with </React.Fragment>
)
There is some different between using <div> and React.Fragment. You can read it here Why are Fragments in React 16 better than container divs?
No need to do it. If you consider JSX to be just functions of React.createElement(...) you can structure the tree you are building in JSX entirely with functions inside functions (hof).
Example:
return React.createElement('div',{
children: React.createElement('div',{
className: classes['login-page'],
children: React.createElement(/* and so on */)
})
})
You can immediately see that you can have started this "creation" of a tree at the second level of the divs. You can also see why the following is not possible similar to the other answers:
return (
React.createElement('div',{})
React.createElement('div',{})
React.createElement('div',{})
)
And no, you don't need to return a single element. You could wrap that for valid javascript into:
return [
React.createElement('div',{ key: 'some_id_1' }),
React.createElement('div',{ key: 'some_id_2' }),
React.createElement('div',{ key: 'some_id_3' })
]
That's why you should wrap everything into a fragment <></>, array or a single wrapper component/jsx-tag. Note the key attribute - React can do some optimisation magic with it.

How to set ref to an unrendered element

I have a login page where I'm placing a button "Watch the video". On click of the button, I'm hiding the button and displaying a video.
Now my requirement is to start playing the video as soon as it displays without having to click the play button.
I'm trying to use refs and somehow I'm unable to set the ref to my video element.
Is there any way to set the refs to unrendered elements in componentDidUpdate?
Please help me!
Following is my code
export default class NewLoginPage extends Component {
constructor(props) {
this.vidRef = React.createRef();
this.state = {
showVideo: false
};
}
handleVideoClick = () => {
this.setState({ showVideo: true })
this.vidRef.current.play();
}
handleCloseClick = () =>{
this.setState({ showVideo: false })
}
render() {
let language_labels = labels["english_labels"]
console.log(labels)
console.log(language_labels)
return (
<div className="container-fluid no-padding">
<div className="new-login-div">
<AppBar className="app-bar">
<Toolbar className="tool-bar">
<img src={pmiLogo} />
{/* <NavLink to="/" id="invoice_upload">LOG OUT</NavLink> */}
{/* <Button className="logout-btn">REGISTER</Button> */}
</Toolbar>
</AppBar>
<Grid container>
<Grid item xs={4}>
<h1 className="welcome-heading">Self Service Portal</h1>
<TextField
className="id-field"
placeholder="Email"
inputProps={{
style: {
color: "#fff",
paddingLeft: "5px"
}
}}
/>
<br />
<Button className="login-btn" endIcon={<ArrowForwardIcon />}>Log In</Button>
</Grid>
<Grid item xs={8}>
<div className="video-div">
{this.state.showVideo && <div>
<IconButton className="close-btn" onClick={(event) => this.handleCloseClick()}>
<CloseIcon />
</IconButton>
<video ref={ref => { this.vidRef = ref }} width="500" height="285" controls className="video-container">
<source src={exampleVid} type="video/mp4" />
Your browser does not support the video tag.
</video>
</div>}
{!this.state.showVideo && <div className="intro-div">
<h5 className="intro-text">
Supporting the vitality and survival of US small businesses—who employ nearly half of the American workforce—is especially critical now. Let’s not forget just how essential they are.
</h5>
<Button
disableRipple={true}
className="video-button"
startIcon={<PlayArrowIcon className="play-icon" />}
onClick={(event) => this.handleVideoClick()}
>
Watch video
</Button>
<br />
<Button
disableRipple={true}
className="reg-button"
startIcon={<ChevronRightIcon className="play-icon" />}>
Register
</Button>
</div>}
</div>
</Grid>
</Grid>
</div>
</div>
);
}
}
React ref property is used to access DOM element reference.
If element is not rendered then DOM element is not created (deleted).
Thus you can't get a ref of something that doesn't exist.
You don't need to use a ref to do that, you can add the autoplay html property to your <video>
for details on autoplay
You can't make use of a ref to an element that is not yet rendered as Anton explained in his answer, the ref.current will be null.
However, it is suggested that you create a separate component for the video and it's logic from that of the login, not only it will solve the issue (the this.state.showVideo condition will stay in the parent component) but it is also how React components should be, for more details refer to this.

How to add a className to all child routes?

I have a Route that has a component with this in its render() :
<div>
<Nav/>
{this.props.children}
<Footer/>
</div>
How do I give all components that are rendered through it as children a className?
I've tried this:
<div>
<Nav/>
{React.cloneElement(this.props.children || <div />, { className: 'test' })}
<Footer/>
</div>
but it doesn't work.
I'm assuming the className went on the Route component instead of the component that was passed to the Route component?