How to Access styles from React? - javascript

I am trying to access the width and height styles of a div in React but I have been running into one problem. This is what I got so far:
componentDidMount() {
console.log(this.refs.container.style);
}
render() {
return (
<div ref={"container"} className={"container"}></div> //set reff
);
}
This works but the output that I get is a CSSStyleDeclaration object and in the all property I can all the CSS selectors for that object but they none of them are set. They are all set to an empty string.
This is the output of the CSSStyleDecleration is: http://pastebin.com/wXRPxz5p
Any help on getting to see the actual styles (event inherrited ones) would be greatly appreciated!
Thanks!

For React v <= 15
console.log( ReactDOM.findDOMNode(this.refs.container).style); //React v > 0.14
console.log( React.findDOMNode(this.refs.container).style);//React v <= 0.13.3
EDIT:
For getting the specific style value
console.log(window.getComputedStyle(ReactDOM.findDOMNode(this.refs.container)).getPropertyValue("border-radius"));// border-radius can be replaced with any other style attributes;
For React v>= 16
assign ref using callback style or by using createRef().
assignRef = element => {
this.container = element;
}
getStyle = () => {
const styles = this.container.style;
console.log(styles);
// for getting computed styles
const computed = window.getComputedStyle(this.container).getPropertyValue("border-radius"));// border-radius can be replaced with any other style attributes;
console.log(computed);
}

Here is an example of computing the CSS property value via React Refs and .getComputedStyle method:
class App extends React.Component {
constructor(props) {
super(props)
this.divRef = React.createRef()
}
componentDidMount() {
const styles = getComputedStyle(this.divRef.current)
console.log(styles.color) // rgb(0, 0, 0)
console.log(styles.width) // 976px
}
render() {
return <div ref={this.divRef}>Some Text</div>
}
}

It's worth noting that while ReactDOM.findDOMNode is usable today, it will be deprecated in the future in place of callback refs.
There is a post here by Dan Abramov which outlines reasons for not using findDOMNode while providing examples of how to replace the use of ReactDOM.findDOMNode with callback refs.
Since I've seen SO users get upset when only a link was included in an answer, so I will pass along one of the examples Dan has kindly provided:
findDOMNode(stringDOMRef)
**Before:**
class MyComponent extends Component {
componentDidMount() {
findDOMNode(this.refs.something).scrollIntoView();
}
render() {
return (
<div>
<div ref='something' />
</div>
)
}
}
**After:**
class MyComponent extends Component {
componentDidMount() {
this.something.scrollIntoView();
}
render() {
return (
<div>
<div ref={node => this.something = node} />
</div>
)
}
}

You should use ReactDOM.findDOMNode method and work from there. Here's the code that does what you need.
var Hello = React.createClass({
componentDidMount: function() {
var elem = ReactDOM.findDOMNode(this.refs.container);
console.log(elem.offsetWidth, elem.offsetHeight);
},
render: function() {
return (
<div ref={"container"} className={"container"}>
Hello world
</div>
);
}
});
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
jsFiddle

In case of function components use the useRef Hook:
const _optionsButton = useRef(null);
const _onSelectText = (event) => {
if (true) {
_optionsButton.current.style["display"] = "block";
} else {
_optionsButton.current.style["display"] = "none";
}
console.log(_optionsButton.current.style); //all styles applied to element
};
add ref attribute to component
<IconButton
style={{
color: "rgba(0, 0, 0, 0.54)",
fill: "rgba(0, 0, 0, 0.54)",
border: "1px solid rgba(0, 0, 0, 0.54)",
position: "absolute",
display: "none",
}}
color="primary"
component="span"
onClick={() => {}}
ref={_optionsButton} //this
>
Check
</IconButton>;
Thank you

You already get the style, the reason why CSSStyleDeclaration object's props have so much empty string value is it's link to the inner style.
See what will happen if you make change like below:
<div ref={"container"} className={"container"} style={{ width: 100 }}></div>

Related

Render unique divs for each hovered element

minimum reproducible example: https://codesandbox.io/s/react-hover-example-tu1eu?file=/index.js
I currently have a new element being rendered when either of 2 other elements are hovered over. But i would like to render different things based upon which element is hovered.
In the example below and in the codepen, there are 2 hoverable divs that are rendered; when they are hovered over, it changes the state and another div is rendered. I would like for the HoverMe2 div to render text "hello2". Currently, whether i hover hoverme1 or 2, they both just render the text "hello".
import React, { Component } from "react";
import { render } from "react-dom";
class HoverExample extends Component {
constructor(props) {
super(props);
this.handleMouseHover = this.handleMouseHover.bind(this);
this.state = {
isHovering: false
};
}
handleMouseHover() {
this.setState(this.toggleHoverState);
}
toggleHoverState(state) {
return {
isHovering: !state.isHovering
};
}
render() {
return (
<div>
<div
onMouseEnter={this.handleMouseHover}
onMouseLeave={this.handleMouseHover}
>
Hover Me
</div>
<div
onMouseEnter={this.handleMouseHover}
onMouseLeave={this.handleMouseHover}
>
Hover Me2
</div>
{this.state.isHovering && <div>hello</div>}
</div>
);
}
}
render(<HoverExample />, document.getElementById("root"));
You need to keep the state of item which you have hovered that's for sure
const { Component, useState, useEffect } = React;
class HoverExample extends Component {
constructor(props) {
super(props);
this.handleMouseHover = this.handleMouseHover.bind(this);
this.state = {
isHovering: false,
values: ['hello', 'hello2'],
value: 'hello'
};
}
handleMouseHover({target: {dataset: {id}}}) {
this.setState(state => {
return {
...state,
isHovering: !state.isHovering,
value: state.values[id]
};
});
}
render() {
return (
<div>
<div
data-id="0"
onMouseEnter={this.handleMouseHover}
onMouseLeave={this.handleMouseHover}
>
Hover Me
</div>
<div
data-id="1"
onMouseEnter={this.handleMouseHover}
onMouseLeave={this.handleMouseHover}
>
Hover Me2
</div>
{this.state.isHovering && <div>{this.state.value}</div>}
</div>
);
}
}
ReactDOM.render(
<HoverExample />,
document.getElementById('root')
);
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<div id="root"></div>
You can pass the context text as shown in example. This is working code:
import React, { Component } from "react";
import { render } from "react-dom";
// Drive this using some configuration. You can set based on your requirement.
export const HOVER_Hello1 = "Hello1";
export const HOVER_Hello2 = "Hello2";
class HoverExample extends Component {
constructor(props) {
super(props);
this.handleMouseHover = this.handleMouseHover.bind(this);
this.state = {
isHovering: false,
contextText: ""
};
}
handleMouseHover = (e, currentText) => {
this.setState({
isHovering: !this.state.isHovering,
contextText: currentText
});
}
toggleHoverState(state) {
//
}
render() {
return (
<div>
<div
onMouseEnter={e => this.handleMouseHover(e, HOVER_Hello1)}
onMouseLeave={e => this.handleMouseHover(e, HOVER_Hello1)}
>
Hover Me
</div>
<div
onMouseEnter={e => this.handleMouseHover(e, HOVER_Hello2)}
onMouseLeave={e => this.handleMouseHover(e, HOVER_Hello2)}
>
Hover Me2
</div>
{this.state.isHovering && <div>{this.state.contextText}</div>}
</div>
);
}
}
export default HoverExample;
If the whole point is about linking dynamically messages to JSX-element you're hovering, you may store that binding (e.g. within an object).
Upon rendering, you simply pass some anchor (e.g. id property of corresponding object) within a custom attribute (data-*), so that later on you may retrieve that, look up for the matching object, put linked message into state and render the message.
Following is a quick demo:
const { Component } = React,
{ render } = ReactDOM,
rootNode = document.getElementById('root')
const data = [
{id:0, text: 'Hover me', message: 'Thanks for hovering'},
{id:1, text: 'Hover me too', message: 'Great job'}
]
class HoverableDivs extends Component {
state = {
messageToShow: null
}
enterHandler = ({target:{dataset:{id:recordId}}}) => {
const {message} = this.props.data.find(({id}) => id == recordId)
this.setState({messageToShow: message})
}
leaveHandler = () => this.setState({messageToShow: null})
render(){
return (
<div>
{
this.props.data.map(({text,id}) => (
<div
key={id}
data-id={id}
onMouseEnter={this.enterHandler}
onMouseLeave={this.leaveHandler}
>
{text}
</div>
))
}
{
this.state.messageToShow && <div>{this.state.messageToShow}</div>
}
</div>
)
}
}
render (
<HoverableDivs {...{data}} />,
rootNode
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>
As #CevaComic pointed out, you can do this with CSS. But if you want to use React, for example, because your actual problem is more complex, here is the answer.
You will need a way to tell apart the two elements. It could be done with some neat tricks, like setting an unique id to each element, passing a custom argument, or something else.
But I would advise against "cool tricks" as it's more difficult to understand what is going on, and the code is more prone to errors. I think the best way it to use a dumb approach of unique functions for unique elements.
Each onMouseEnter and onMouseLeave has to be an unique function (e.g. handleMouseHover1 and handleMouseHover2), and each of those functions need to control unique state (for example, isHovering1 and isHovering2). Then you have to render the element you want based on the state. Of course, for a real-world code, you will probably want to use more descriptive names to make the code more comprehensible. The full code would look something like this.
class HoverExample extends Component {
state = {
isHovering1: false,
isHovering2: false
};
handleMouseHover1 = () => {
this.setState(({ isHovering1 }) => ({ isHovering1: !isHovering1 }));
};
handleMouseHover2 = () => {
this.setState(({ isHovering2 }) => ({ isHovering2: !isHovering2 }));
};
render() {
const { isHovering1, isHovering2 } = this.state;
return (
<div>
<div
onMouseEnter={this.handleMouseHover1}
onMouseLeave={this.handleMouseHover1}
>
Hover Me1
</div>
<div
onMouseEnter={this.handleMouseHover2}
onMouseLeave={this.handleMouseHover2}
>
Hover Me2
</div>
{isHovering1 && <div>hello1</div>}
{isHovering2 && <div>hello2</div>}
</div>
);
}
}
Also, updated example: https://codesandbox.io/s/react-hover-example-rc3h0
Note: I have also edited the code to add some syntax sugar which exists with newer ECMAScript versions. Instead of binding the function, you can use the arrow function format, e.g. fn = () => { ... }. The arrow function means the this context is automatically bound to the function, so you don't have to do it manually. Also, you don't have to initialize this.state inside the constructor, you can define it as a class instance property. With those two things together, you do not need the constructor at all, and it makes the code a bit cleaner.

react-native : Can I use props in a method?

I have my class and I have a method and I am wondering if I could use props inside a mehtod.
Notice I try to use props in methodTwo. Is this possible? If not, is there a way I could use props in method?
import React from 'react';
import { Image, Text, View } from 'react-native';
export default class Test extends React.PureComponent {
methodOne = () => {
this.setState({
one:false,
two:false,
three:false
})
}
methodTwo = () => {
this.setState({
one:false,
two:false,
//I want to use props
three:this.props.three
})
}
render() {
return (
<View style={{ backgroundColor: 'transparent', alignItems: 'center' }}>
<Button title='one' onPress={()=>this.methodOne()}/>
// I could call i like this?
<Test three='newState'/>
</View>
);
}
}
methodTwo = () => {
this.setState({
one:false,
two:false,
three:this.props.three
})
}
props-> is the value that is been transferred from parent component to child component.
In class based component you fetch the value by using this.props.Attribute_name and in functional based component you can fetch the value using props.Attribute_name (mind functional based component dont have any concept of this)
if you want to use this.props.three ,then in parent component call (the component calling this particular component) <Test three="anyValue" /> then you can easily get this value in child component.
class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
<img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
);
}
}
class MouseWithCat extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
{/*
We could just swap out the <p> for a <Cat> here ... but then
we would need to create a separate <MouseWithSomethingElse>
component every time we need to use it, so <MouseWithCat>
isn't really reusable yet.
*/}
<Cat mouse={this.state} />
</div>
);
}
}
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<MouseWithCat />
</div>
);
}
}
The props are accessible to whole of the class scope with the syntax this.props.xxxx if you have passed it from its parent component. SO you can use in methodOne too.
You can use props inside a method. Any specific error you are facing ?.

Using React to change background color of a body element on click?

I am trying to change the background color of html body to red with a button click,
but i only know how to do it with an element that is already inside body, not the body itself.
class App extends Component {
constructor(props) {
super(props);
this.state = {
bgColor: ""
};
}
boxClick = e => {
this.setState({
bgColor: "red"
});
};
render() {
return (
<div className="App">
<article className="experimentsHolder">
<div
className="boxClickCss"
style={{ backgroundColor: this.state.bgColor }}
onClick={this.boxClick}
>
Click Me!
</div>
</article>
</div>
);
}
}
As you can see, i added style={{backgroundColor: this.state.bgColor}} to div, but i can't add it inside body since it's not in this file. Any help?
It is usually a good practice to manipulate the DOM inside lifecycle methods See doc here. If this is important to you, you could use componentDidUpdate lifecycle method on your App.js component and from there use standard dom manipulation to find the body and update its background color. You can also check in the method to ensure that the previous state and current state changed before acting on it. It could look something like this:
class App extends Component {
constructor(props) {
super(props);
this.state = {
bgColor: ""
}
}
componentDidUpdate(prevProps, prevState){
const { bgcolor } = this.state;
if(prevProps.bgColor !== bgColor){
const bodyElt = document.querySelector("body");
bodyElt.style.backgroundColor = bgcolor;
}
}
Here's an example of manipulating the body within a React component, adapted from Frederic Caplette's answer recommending to do so in lifecycle methods. However, I do agree with Erik Philips's comment above that this kind of manipulation is better done via adding/removing CSS classes. Using CSS classes decouples details of the styles from the component logic and enables extensibility without affecting that logic.
const docBody = document.querySelector('body');
class App extends React.Component {
constructor(props) {
super(props);
this.state = {bgColored: false};
this.colorBackground = this.colorBackground.bind(this);
this.clearBackground = this.clearBackground.bind(this);
}
colorBackground() {
this.setState({bgColored: true});
}
clearBackground() {
this.setState({bgColored: false});
}
componentDidUpdate(prevProps, prevState) {
const { bgColored } = this.state;
const className = 'redBg';
if(prevState.bgColored !== bgColored){
bgColored ?
docBody.classList.add(className) :
docBody.classList.remove(className);
}
}
render() {
return (
<div>
<a href="#" onClick={() => this.colorBackground()}>Turn Red</a>
{' | '}
<a href="#" onClick={() => this.clearBackground()}>Reset</a>
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('root'));
.redBg {
background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<body>
<div id="root"></div>
</body>

How to extract JSX from class render method?

I want to write something like SCSS for React Native: it'll parse your component jsx and the special SCSS-like styles and return a usual RN component with reworked styles and jsx.
Lets say we have this react code:
class MyClass extends Component {
render() {
return (
<View style={styles.container}>
<Text>I remember syrup sandwiches</Text>
</View>
);
}
}
Also I have SCSS-ish styles where every Text component inside the parent with a container "class" will have the same props that we provided.
const styles = StyleSheet.create(
toRNStyles({
container: {
Text: { color: 'red' },
},
})
);
In the end we need the output of something like this:
...
<View style={styles.container}>
<Text style={styles._Text_container}>
I remember syrup sandwiches
</Text>
</View>
...
So how can I get the jsx that's returning from the render method from outside the class?
You might write a plugin for babel, as react-native uses it to transform JSX to plain javascript.
Have a look to the these packages:
babel-helper-builder-react-jsx
babel-plugin-syntax-jsx
babel-plugin-transform-react-jsx
babel-plugin-transform-react-jsx-source
jsx-ast-utils
There doesn't seem to be a standard way of doing this. However, you could import ReactDOMServer and use its renderToStaticMarkup function.
Like this:
class MyApp extends React.Component {
render() {
var myTestComponent = <Test>bar</Test>;
console.dir(ReactDOMServer.renderToStaticMarkup(myTestComponent));
return myTestComponent;
}
}
const Test = (props) => {
return (
<div>
<p>foo</p>
<span>{props.children}</span>
</div>
);
}
ReactDOM.render(<MyApp />, document.getElementById("myApp"));
<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>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom-server.js"></script>
<div id="myApp"></div>
I think parsing the returned element is the wrong approach. One challenge will be that the value of style will be an object (styles.container === a hash of style key/values) whereas you need a key which can be mapped to the object.
I think the most reusable approach is to leverage React context (which I'm assuming RN supports!) to build a styleName which can be augmented as you got down the component tree.
Here's an initial approach which makes a few assumptions (e.g. that every component will have styleName provided as a prop; you might want to provide that at design-time rather than run-time). In short, you wrap every component you want to participate with this HOC and the provide styleName as a prop to each component. Those styleName values are concatenated to produce contextualized names which are mapped to styles.
This example produces:
<div style="background-color: green; color: red;">
<div style="color: blue;">Some Text</div>
</div>
const CascadingStyle = (styles, Wrapped) => class extends React.Component {
static displayName = 'CascadingStyle';
static contextTypes = {
styleName: React.PropTypes.string
}
static childContextTypes = {
styleName: React.PropTypes.string
}
// pass the current styleName down the component tree
// to other instances of CascadingStyle
getChildContext () {
return {
styleName: this.getStyleName()
};
}
// generate the current style name by either using the
// value from context, joining the context value with
// the current value, or using the current value (in
// that order).
getStyleName () {
const {styleName: contextStyleName} = this.context;
const {styleName: propsStyleName} = this.props;
let styleName = contextStyleName;
if (propsStyleName && contextStyleName) {
styleName = `${contextStyleName}_${propsStyleName}`;
} else if (propsStyleName) {
styleName = propsStyleName;
}
return styleName;
}
// if the component has styleName, find that style object and merge it with other run-time styles
getStyle () {
if (this.props.styleName) {
return Object.assign({}, styles[this.getStyleName()], this.props.styles);
}
return this.props.styles;
}
render () {
return (
<Wrapped {...this.props} style={this.getStyle()} />
);
}
};
const myStyles = {
container: {backgroundColor: 'green', color: 'red'},
container_text: {color: 'blue'}
};
const Container = CascadingStyle(myStyles, (props) => {
return (
<div {...props} />
);
});
const Text = CascadingStyle(myStyles, (props) => {
return (
<div {...props} />
);
});
const Component = () => {
return (
<Container styleName="container">
<Text styleName="text">Some Text</Text>
</Container>
);
};
<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>

How to scroll to an element?

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>

Categories

Resources