I am learning through an open source project here. I have deployed it and it works. So the below pasted code is valid for sure.
I was looking at a Header component in Header.js:
class Header extends React.Component {
state = {
open: false,
};
render() {
const {
classes,
toggleDrawerOpen,
margin,
turnDarker,
} = this.props;
return (
.... some code ....
)
I see that classes is passed as a prop from the parent. So I looked into the parent component, Dashboard. Here is the code:
import { Header, Sidebar, BreadCrumb } from './../../components';
import { toggleAction, openAction, playTransitionAction } from './../../actions/UiActions';
import styles from './appStyles-jss';
class Dashboard extends React.Component {
state = {
transform: 0,
};
componentDidMount = () => {
// Scroll content to top
const mainContent = document.getElementById('mainContent');
mainContent.addEventListener('scroll', this.handleScroll);
// Set expanded sidebar menu
const currentPath = this.props.history.location.pathname;
this.props.initialOpen(currentPath);
// Play page transition
this.props.loadTransition(true);
// Execute all arguments when page changes
this.unlisten = this.props.history.listen(() => {
mainContent.scrollTo(0, 0);
setTimeout(() => {
this.props.loadTransition(true);
}, 500);
});
}
componentWillUnmount() {
const mainContent = document.getElementById('mainContent');
mainContent.removeEventListener('scroll', this.handleScroll);
}
handleScroll = (event) => {
const scoll = event.target.scrollTop;
this.setState({
transform: scoll
});
}
render() {
const {
classes, // classes is here
route,
toggleDrawer,
sidebarOpen,
loadTransition,
pageLoaded
} = this.props;
const darker = true;
return (
<div className={classes.appFrameInner}>
// NOTE: Header component is here but I don't see how classes is passed to it.
<Header toggleDrawerOpen={toggleDrawer} turnDarker={this.state.transform > 30 && darker} margin={sidebarOpen} />
<Sidebar
open={sidebarOpen}
toggleDrawerOpen={toggleDrawer}
loadTransition={loadTransition}
turnDarker={this.state.transform > 30 && darker}
/>
<main className={classNames(classes.content, !sidebarOpen && classes.contentPadding)} id="mainContent">
<div className={classes.bgbar} />
</main>
</div>
);
}
}
You can see that the classes prop is passed from Dashboard's parent. However, I was expecting some syntax that shows it is passed into the child Header component.
See the "NOTE" line in the code, nothing was said about passing the entire props to Header component or passing the const classes specifically to Header.
How is classes passed from parent (Dashbaord) to child (Header)?
The classes prop is not passed from parent Dashboard to child Header.
The classes prop is made available directly to your Header component using the wrapping withStyles HOC when exporting your component:
export default withStyles(styles)(Header);
This approach is commonly known as CSS-in-JS and you can read more details in the material-ui styles documentation.
I am trying to pass state from parent to child using React, however both components are imported and therefor the state variables of the parent component are not declared.
I have two components both exported from the same file. The first component is a wrapper for the second. This component has a useEffect function which find its height and width and set these values to hook state.
export const TooltipWrapper = ({ children, ariaLabel, ...props }) => {
const [width, setWidth] = React.useState(0);
const [height, setHeight] = React.useState(0);
const ref = React.useRef(null);
React.useEffect(() => {
if (ref.current && ref.current.getBoundingClientRect().width) {
setWidth(ref.current.getBoundingClientRect().width);
}
if (ref.current && ref.current.getBoundingClientRect().height) {
setHeight(ref.current.getBoundingClientRect().height);
}
});
return <TooltipDiv>{children}</TooltipDiv>;
The next component which is exported from the same file looks like this
export const Tooltip = ({
ariaLabel,
icon,
iconDescription,
text,
modifiers,
wrapperWidth,
}) => {
return (
<TooltipContainer
aria-label={ariaLabel}
width={wrapperWidth}
>
<TooltipArrow data-testid="tooltip-arrow" modifiers={modifiers} />
<TooltipLabel
aria-label={ariaLabel}
>
{text}
</TooltipLabel>
</TooltipContainer>
);
};
The component Tooltip is expecting a prop wrapperWidth. This is where I want to pass in the width hook value from the TooltipWrapper component.
Both components are imported into my App component
import React from "react";
import { GlobalStyle } from "./pattern-library/utils";
import { Tooltip, TooltipWrapper } from "./pattern-library/components/";
function App() {
return (
<div className="App">
<div style={{ padding: "2rem", position: "relative" }}>
<TooltipWrapper>
<button style={{ position: "relative" }}>click </button>
<Tooltip
modifiers={["right"]}
text="changing width"
wrapperWidth={width}
/>
</TooltipWrapper>
</div>
</div>
);
}
Here I am told that width is not defined, which I expect since I'm not declaring width in this file.
Does anyone have an idea of how I can access the width and height state value for the parent component within the App file?
Render Props could work:
Add a renderTooltip prop to <TooltipWrapper>:
<TooltipWrapper renderTooltip={({ width }) => <Tooltip ...existing wrapperWidth={width} />}>
<button style={{ position: 'relative' }}>click</button>
</TooltipWrapper>
NB. ...existing is just the other props you are using with Tooltip
And then update the return of <TooltipWrapper>:
return (
<TooltipDiv>
{children}
props.renderTooltip({ width });
</TooltipDiv>
);
I am trying to use react-virtualized to virtualize a list where some rows have varying heights, and also the list takes up all the space in the parent. I am trying to accomplish this with the CellMeasurer, AutoSizer and List components.
My package versions are as follows:
react: "16.8.6"
react-dom: "16.8.6"
react-virtualized: "^9.21.1"
import React, { PureComponent } from 'react';
import 'react-virtualized/styles.css';
import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer';
import { CellMeasurer, CellMeasurerCache, List } from 'react-virtualized';
class Table extends PureComponent {
rowRenderer = ({ index, style, key }) => {
return (
<CellMeasurer
cache={this.cache}
columnIndex={0}
key={key}
parent={parent}
rowIndex={index}
>
<div style={style} key={key}>
content
</div>
</CellMeasurer>
);
}
cache = new CellMeasurerCache({
defaultHeight: 24,
fixedWidth: true,
});
renderAutoSizerContent = () => {
return this.RenderList;
}
RenderList = ({ height, width }) => {
return (<List
items={this.props.items}
width={width}
height={height}
rowCount={this.props.items.length}
rowHeight={this.cache.rowHeight}
rowRenderer={this.rowRenderer}
deferredMeasurementCache={this.cache}
/>
);
}
render() {
return (
<AutoSizer
items={ this.props.items}
>
{this.renderAutoSizerContent()}
</AutoSizer>
);
}
}
export default Table;
After the initial render everything looks like this. For some reason the top attribute is 0 for every element in the array:
After scrolling or triggering a rerender the items seem to get their top property and the following renders. In the actual code some of my elements have height variance, but the height seems to default to the defaultHeight I give to the CellMeasurerCache constructor anyway.
How do I get the initial render to have top property for each element, and how do I get the heights to calculate correctly? What am I doing wrong in the code I have shown here?
In your rowRenderer component, you provided parent prop to CellMeasurer but parent is undefined.
You get parent from rowRenderer as written in the docs: https://github.com/bvaughn/react-virtualized/blob/master/docs/List.md#rowrenderer
So your rowRenderer component should be:
rowRenderer = ({ index, style, key, parent }) => {
return (
<CellMeasurer
cache={this.cache}
columnIndex={0}
key={key}
parent={parent}
rowIndex={index}
>
<div style={style} key={key}>
{items[index]}
</div>
</CellMeasurer>
);
}
You can also check this code sandbox with working example: https://codesandbox.io/s/material-demo-yw1k8?fontsize=14
I have a chat widget that pulls up an array of messages every time I scroll up. The problem I am facing now is the slider stays fixed at the top when messages load. I want it to focus on the last index element from the previous array. I figured out that I can make dynamic refs by passing index, but I would also need to know what kind of scroll function to use to achieve that
handleScrollToElement(event) {
const tesNode = ReactDOM.findDOMNode(this.refs.test)
if (some_logic){
//scroll to testNode
}
}
render() {
return (
<div>
<div ref="test"></div>
</div>)
}
React 16.8 +, Functional component
const ScrollDemo = () => {
const myRef = useRef(null)
const executeScroll = () => myRef.current.scrollIntoView()
// run this function from an event handler or an effect to execute scroll
return (
<>
<div ref={myRef}>Element to scroll to</div>
<button onClick={executeScroll}> Click to scroll </button>
</>
)
}
Click here for a full demo on StackBlits
React 16.3 +, Class component
class ReadyToScroll extends Component {
constructor(props) {
super(props)
this.myRef = React.createRef()
}
render() {
return <div ref={this.myRef}>Element to scroll to</div>
}
executeScroll = () => this.myRef.current.scrollIntoView()
// run this method to execute scrolling.
}
Class component - Ref callback
class ReadyToScroll extends Component {
render() {
return <div ref={ (ref) => this.myRef=ref }>Element to scroll to</div>
}
executeScroll = () => this.myRef.scrollIntoView()
// run this method to execute scrolling.
}
Don't use String refs.
String refs harm performance, aren't composable, and are on their way out (Aug 2018).
string refs have some issues, are considered legacy, and are likely to
be removed in one of the future releases. [Official React documentation]
resource1resource2
Optional: Smoothe scroll animation
/* css */
html {
scroll-behavior: smooth;
}
Passing ref to a child
We want the ref to be attached to a dom element, not to a react component. So when passing it to a child component we can't name the prop ref.
const MyComponent = () => {
const myRef = useRef(null)
return <ChildComp refProp={myRef}></ChildComp>
}
Then attach the ref prop to a dom element.
const ChildComp = (props) => {
return <div ref={props.refProp} />
}
this worked for me
this.anyRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' })
EDIT: I wanted to expand on this based on the comments.
const scrollTo = (ref) => {
if (ref && ref.current /* + other conditions */) {
ref.current.scrollIntoView({ behavior: 'smooth', block: 'start' })
}
}
<div ref={scrollTo}>Item</div>
I had a simple scenario, When user clicks on the menu item in my Material UI Navbar I want to scroll them down to the section on the page. I could use refs and thread them through all the components but I hate threading props through multiple components because that makes code fragile.
I just used vanilla JS in my react component, turns out it works just fine. Placed an ID on the element I wanted to scroll to and in my header component I just did this.
const scroll = () => {
const section = document.querySelector( '#contact-us' );
section.scrollIntoView( { behavior: 'smooth', block: 'start' } );
};
Just find the top position of the element you've already determined https://www.w3schools.com/Jsref/prop_element_offsettop.asp then scroll to this position via scrollTo method https://www.w3schools.com/Jsref/met_win_scrollto.asp
Something like this should work:
handleScrollToElement(event) {
const tesNode = ReactDOM.findDOMNode(this.refs.test)
if (some_logic){
window.scrollTo(0, tesNode.offsetTop);
}
}
render() {
return (
<div>
<div ref="test"></div>
</div>)
}
UPDATE:
since React v16.3 the React.createRef() is preferred
constructor(props) {
super(props);
this.myRef = React.createRef();
}
handleScrollToElement(event) {
if (<some_logic>){
window.scrollTo(0, this.myRef.current.offsetTop);
}
}
render() {
return (
<div>
<div ref={this.myRef}></div>
</div>)
}
You can now use useRef from react hook API
https://reactjs.org/docs/hooks-reference.html#useref
declaration
let myRef = useRef()
component
<div ref={myRef}>My Component</div>
Use
window.scrollTo({ behavior: 'smooth', top: myRef.current.offsetTop })
Jul 2019 - Dedicated hook/function
A dedicated hook/function can hide implementation details, and provides a simple API to your components.
React 16.8 + Functional Component
const useScroll = () => {
const elRef = useRef(null);
const executeScroll = () => elRef.current.scrollIntoView();
return [executeScroll, elRef];
};
Use it in any functional component.
const ScrollDemo = () => {
const [executeScroll, elRef] = useScroll()
useEffect(executeScroll, []) // Runs after component mounts
return <div ref={elRef}>Element to scroll to</div>
}
full demo
React 16.3 + class Component
const utilizeScroll = () => {
const elRef = React.createRef();
const executeScroll = () => elRef.current.scrollIntoView();
return { executeScroll, elRef };
};
Use it in any class component.
class ScrollDemo extends Component {
constructor(props) {
super(props);
this.elScroll = utilizeScroll();
}
componentDidMount() {
this.elScroll.executeScroll();
}
render(){
return <div ref={this.elScroll.elRef}>Element to scroll to</div>
}
}
Full demo
Using findDOMNode is going to be deprecated eventually.
The preferred method is to use callback refs.
github eslint
The nicest way is to use element.scrollIntoView({ behavior: 'smooth' }). This scrolls the element into view with a nice animation.
When you combine it with React's useRef(), it can be done the following way.
import React, { useRef } from 'react'
const Article = () => {
const titleRef = useRef()
function handleBackClick() {
titleRef.current.scrollIntoView({ behavior: 'smooth' })
}
return (
<article>
<h1 ref={titleRef}>
A React article for Latin readers
</h1>
// Rest of the article's content...
<button onClick={handleBackClick}>
Back to the top
</button>
</article>
)
}
When you would like to scroll to a React component, you need to forward the ref to the rendered element. This article will dive deeper into the problem.
You can also use scrollIntoView method to scroll to a given element.
handleScrollToElement(event) {
const tesNode = ReactDOM.findDOMNode(this.refs.test)
if (some_logic){
tesNode.scrollIntoView();
}
}
render() {
return (
<div>
<div ref="test"></div>
</div>)
}
I might be late to the party but I was trying to implement dynamic refs to my project the proper way and all the answer I have found until know aren't quiet satisfying to my liking, so I came up with a solution that I think is simple and uses the native and recommended way of react to create the ref.
sometimes you find that the way documentation is wrote assumes that you have a known amount of views and in most cases this number is unknown so you need a way to solve the problem in this case, create dynamic refs to the unknown number of views you need to show in the class
so the most simple solution i could think of and worked flawlessly was to do as follows
class YourClass extends component {
state={
foo:"bar",
dynamicViews:[],
myData:[] //get some data from the web
}
inputRef = React.createRef()
componentDidMount(){
this.createViews()
}
createViews = ()=>{
const trs=[]
for (let i = 1; i < this.state.myData.lenght; i++) {
let ref =`myrefRow ${i}`
this[ref]= React.createRef()
const row = (
<tr ref={this[ref]}>
<td>
`myRow ${i}`
</td>
</tr>
)
trs.push(row)
}
this.setState({dynamicViews:trs})
}
clickHandler = ()=>{
//const scrollToView = this.inputRef.current.value
//That to select the value of the inputbox bt for demostrate the //example
value=`myrefRow ${30}`
this[value].current.scrollIntoView({ behavior: "smooth", block: "start" });
}
render(){
return(
<div style={{display:"flex", flexDirection:"column"}}>
<Button onClick={this.clickHandler}> Search</Button>
<input ref={this.inputRef}/>
<table>
<tbody>
{this.state.dynamicViews}
<tbody>
<table>
</div>
)
}
}
export default YourClass
that way the scroll will go to whatever row you are looking for..
cheers and hope it helps others
This solution works for me in ReactJS
In header.js
function scrollToTestDiv(){
const divElement = document.getElementById('test');
divElement.scrollIntoView({ behavior: 'smooth' });
}
<a class="nav-link" onClick={scrollToTestDiv}> Click here! </a>
In index.html
<div id="test"></div>
You could try this way:
handleScrollToElement = e => {
const elementTop = this.gate.offsetTop;
window.scrollTo(0, elementTop);
};
render(){
return(
<h2 ref={elem => (this.gate = elem)}>Payment gate</h2>
)}
You can use something like componentDidUpdate
componentDidUpdate() {
var elem = testNode //your ref to the element say testNode in your case;
elem.scrollTop = elem.scrollHeight;
};
Here is the Class Component code snippet you can use to solve this problem:
This approach used the ref and also scrolls smoothly to the target ref
import React, { Component } from 'react'
export default class Untitled extends Component {
constructor(props) {
super(props)
this.howItWorks = React.createRef()
}
scrollTohowItWorks = () => window.scroll({
top: this.howItWorks.current.offsetTop,
left: 0,
behavior: 'smooth'
});
render() {
return (
<div>
<button onClick={() => this.scrollTohowItWorks()}>How it works</button>
<hr/>
<div className="content" ref={this.howItWorks}>
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nesciunt placeat magnam accusantium aliquid tenetur aspernatur nobis molestias quam. Magnam libero expedita aspernatur commodi quam provident obcaecati ratione asperiores, exercitationem voluptatum!
</div>
</div>
)
}
}
If anyone is using Typescript, here is Ben Carp's answer for it:
import { RefObject, useRef } from 'react';
export const useScroll = <T extends HTMLElement>(
options?: boolean | ScrollIntoViewOptions
): [() => void, RefObject<T>] => {
const elRef = useRef<T>(null);
const executeScroll = (): void => {
if (elRef.current) {
elRef.current.scrollIntoView(options);
}
};
return [executeScroll, elRef];
};
You can use useRef along with scrollIntoView.
use useReffor the element you want to scroll to: here I want to sroll to the PieceTabs element that is why I wrap it with a Box(div) so I can get access to the dom elemnt
You might be familiar with refs primarily as a way to access the DOM. If you pass a ref object to React with , React will set its .current property to the corresponding DOM node whenever that node changes. See the doc
...
const tabsRef = useRef()
...
<Box ref={tabsRef}>
<PieceTabs piece={piece} value={value} handleChange={handleChange} />
</Box>
...
Create a function that handle this sroll:
const handleSeeCompleteList = () => {
const tabs = tabsRef.current
if (tabs) {
tabs.scrollIntoView({
behavior: 'smooth',
block: 'start',
})
}
}
Call this function on the element you want once you click to scroll to the target:
<Typography
variant="body2"
sx={{
color: "#007BFF",
cursor: "pointer",
fontWeight: 500,
}}
onClick={(e) => {
handleChange(e, 2);
handleSeeCompleteList(); // here we go
}}
>
Voir toute la liste
</Typography>;
And here we go
<div id="componentToScrollTo"><div>
<a href='#componentToScrollTo'>click me to scroll to this</a>
Follow these steps:
1) Install:
npm install react-scroll-to --save
2) Import the package:
import { ScrollTo } from "react-scroll-to";
3) Usage:
class doc extends Component {
render() {
return(
<ScrollTo>
{({ scroll }) => (
<a onClick={() => scroll({ x: 20, y: 500, , smooth: true })}>Scroll to Bottom</a>
)}
</ScrollTo>
)
}
}
I used this inside a onclick function to scroll smoothly to a div where its id is "step2Div".
let offset = 100;
window.scrollTo({
behavior: "smooth",
top:
document.getElementById("step2Div").getBoundingClientRect().top -
document.body.getBoundingClientRect().top -
offset
});
After reading through manny forums found a really easy solution.
I use redux-form. Urgo mapped redux-from fieldToClass. Upon error I navigate to the first error on the list of syncErrors.
No refs and no third party modules. Just simple querySelector & scrollIntoView
handleToScroll = (field) => {
const fieldToClass = {
'vehicleIdentifier': 'VehicleIdentifier',
'locationTags': 'LocationTags',
'photos': 'dropzoneContainer',
'description': 'DescriptionInput',
'clientId': 'clientId',
'driverLanguage': 'driverLanguage',
'deliveryName': 'deliveryName',
'deliveryPhone': 'deliveryPhone',
"deliveryEmail": 'deliveryEmail',
"pickupAndReturn": "PickupAndReturn",
"payInCash": "payInCash",
}
document?.querySelector(`.${fieldToClasses[field]}`)
.scrollIntoView({ behavior: "smooth" })
}
In order to automatically scroll into the particular element, first need to select the element using document.getElementById and then we need to scroll using scrollIntoView(). Please refer the below code.
scrollToElement= async ()=>{
document.getElementById('id001').scrollIntoView();
}
The above approach worked for me.
If you want to do it on page load you can use useLayoutEffect, and useRef.
import React, { useRef, useLayoutEffect } from 'react'
const ScrollDemo = () => {
const myRef = useRef(null)
useLayoutEffect(() => {
window.scrollTo({
behavior: "smooth",
top: myRef.current.offsetTop,
});
}, [myRef.current]);
return (
<>
<div ref={myRef}>I wanna be seen</div>
</>
)
}
What worked for me:
class MyComponent extends Component {
constructor(props) {
super(props);
this.myRef = React.createRef(); // Create a ref
}
// Scroll to ref function
scrollToMyRef = () => {
window.scrollTo({
top:this.myRef.offsetTop,
// behavior: "smooth" // optional
});
};
// On component mount, scroll to ref
componentDidMount() {
this.scrollToMyRef();
}
// Render method. Note, that `div` element got `ref`.
render() {
return (
<div ref={this.myRef}>My component</div>
)
}
}
To anyone else reading this who didn't have much luck with the above solutions or just wants a simple drop-in solution, this package worked for me: https://www.npmjs.com/package/react-anchor-link-smooth-scroll. Happy Hacking!
Just a heads up, I couldn't get these solutions to work on Material UI components. Looks like they don't have the current property.
I just added an empty div amongst my components and set the ref prop on that.
Here is my solution:
I put an invisible div inside main div and made its position absolute. Then set the top value to -(header height) and set the ref on this div. Or you can just react that div with children method.
It's working great so far!
<div className="position-relative">
<div style={{position:"absolute", top:"-80px", opacity:0, pointerEvents:'none'}} ref={ref}></div>
Maybe someone meets situation like me
https://reactjs.org/docs/hooks-faq.html#how-can-i-measure-a-dom-node
How can I measure a DOM node?
One rudimentary way to measure the position or size of a DOM node is to use a callback ref. React will call that callback whenever the ref gets attached to a different node. Here is a small demo:
function MeasureExample() {
const [height, setHeight] = useState(0);
const measuredRef = useCallback(node => {
if (node !== null) {
setHeight(node.getBoundingClientRect().height);// you can scroll in this line
}
}, []);
return (
<>
<h1 ref={measuredRef}>Hello, world</h1>
<h2>The above header is {Math.round(height)}px tall</h2>
</>
);
}
We didn’t choose useRef in this example because an object ref doesn’t notify us about changes to the current ref value. Using a callback ref ensures that even if a child component displays the measured node later (e.g. in response to a click), we still get notified about it in the parent component and can update the measurements.
Note that we pass [] as a dependency array to useCallback. This ensures that our ref callback doesn’t change between the re-renders, and so React won’t call it unnecessarily.
In this example, the callback ref will be called only when the component mounts and unmounts, since the rendered component stays present throughout any rerenders. If you want to be notified any time a component resizes, you may want to use ResizeObserver or a third-party Hook built on it.
<div onScrollCapture={() => this._onScrollEvent()}></div>
_onScrollEvent = (e)=>{
const top = e.nativeEvent.target.scrollTop;
console.log(top);
}
This is the easiest way I find working for me.
Just use normal javascript syntax no need for much packages
const scrollTohowItWorks = () => window.scroll({
top: 2000,
left: 0,
behavior: 'smooth'
});
<NavLink onClick={scrollTohowItWorks} style={({ isActive }) => isActive? {color: '#e26702', fontWeight:'bold'}: { color: '#0651b3'}} to=''>Support</NavLink>